block by armollica e1ae83935b0028587f1e26cf66b9d190

Playing with the Satellite Projection

Full Screen

Playing with the parameters for d3.geoSatellite().

Focusing just on the U.S. at the moment. Hope to expand it for the whole world at some point.

Check out this block by Elijah Meeks for another satellite projection explorer tool.

index.html

<!DOCTYPE html>
<html>
<head>
<style>

html, body {
    font-family: monospace;
    color: #333;
}

.container {
    position: relative;
}

#controls {
    position: absolute;
    top: 0;
    padding: 25px 25px 25px 25px;
    opacity: 0.5;
    transition: opacity 0.1s;
}

#controls:hover {
    opacity: 1;
}

.control {
    display: inline-block;
    position: relative;
}

.control input {
    width: 75px;
}

.control .label {
    text-transform: uppercase;
    font-size: 10px;
    color: #555;
    position: absolute;
    bottom: 100%;
    text-shadow: -2px -2px 1px #fff,
                 -2px -1px 1px #fff,
                 -2px 0px 1px #fff,
                 -2px 1px 1px #fff,
                 -2px 2px 1px #fff,
                 -1px -2px 1px #fff,
                 -1px -1px 1px #fff,
                 -1px 0px 1px #fff,
                 -1px 1px 1px #fff,
                 -1px 2px 1px #fff,
                 0px -2px 1px #fff,
                 0px -1px 1px #fff,
                 0px 1px 1px #fff,
                 0px 2px 1px #fff,
                 1px -2px 1px #fff,
                 1px -1px 1px #fff,
                 1px 0px 1px #fff,
                 1px 1px 1px #fff,
                 1px 2px 1px #fff,
                 2px -2px 1px #fff,
                 2px -1px 1px #fff,
                 2px 0px 1px #fff,
                 2px 1px 1px #fff,
                 2px 2px 1px #fff;
}

</style>
</head>
<body>

<div class="container">
    <div id="controls">
        <div class="control">
            <span class="label">Distance</span>
            <input id="distance" type="number" value="1.11" step="0.01" min="0" max="5">
        </div>
        <div class="control">
            <span class="label">Tilt</span>
            <input id="tilt" type="number" value="21.7" step="0.1" min="0" max="100">
        </div>
        <div class="control">
            <span class="label">Scale</span>
            <input id="scale" type="number" value="5500" step="10">
        </div>
        <div class="control">
            <span class="label">Yaw</span>
            <input id="yaw" type="number" value="70.8" step="0.05">
        </div>
        <div class="control">
            <span class="label">Pitch</span>
            <input id="pitch" type="number" value="-39.95" step="0.05">
        </div>
        <div class="control">
            <span class="label">Roll</span>
            <input id="roll" type="number" value="-87.43" step="0.05">
        </div>
        <div class="control">
            <span class="label">cX</span>
            <input id="cx" type="number" value="1.15" step="0.05">
        </div>
        <div class="control">
            <span class="label">cY</span>
            <input id="cy" type="number" value="10.2" step="0.05">
        </div>
    </div>
    <canvas id="map"></canvas>
</div>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="https://unpkg.com/topojson@3"></script>
<script>

var width = 960,
    height = 960,
    drawMap = function() {}

var canvas = d3.select('canvas#map')
    .attr('width', width)
    .attr('height', height);

var context = canvas.node().getContext('2d');

var projection = d3.geoSatellite()
    .precision(.1);

var controls = d3.select('#controls');

controls.selectAll('input').on('change', updateProjection);

var graticule = d3.geoGraticule().step([3, 3]);

var path = d3.geoPath()
    .projection(projection)
    .pointRadius(2)
    .context(context);

d3.json('topo.json', function (error, topo) {
    if (error) throw error;

    var states = topojson.feature(topo, topo.objects.states),
        urbanAreas = topojson.feature(topo, topo.objects['urban-areas']),
        populatedPlaces = topojson.feature(topo, topo.objects['populated-places']);

    drawMap = function() {
        context.clearRect(0, 0, width, height);

        // Graticule
        context.save();
        context.beginPath();
        path(graticule());
        context.strokeStyle = '#777';
        context.stroke();
        context.restore();

        // States
        context.save();
        context.globalAlpha = 0.8;
        context.beginPath();
        path(states);
        context.strokeStyle = '#555';
        context.fillStyle = '#eee';
        context.stroke();
        context.fill();
        context.restore();

        // Urban areas
        context.save();
        context.beginPath();
        context.globalAlpha = 0.8;
        path(urbanAreas);
        context.fillStyle = '#999';
        context.fill();

        // Populated Places
        context.save();        
        context.beginPath();
        path(populatedPlaces);
        context.strokeStyle = 'darkred';
        context.stroke();
        context.restore();

        context.save();
        context.font = '12px monospace';
        context.textAlign = 'center';
        context.fillStyle = '#333';
        context.shadowColor = '#fff';
        context.shadowBlur = 2;
        populatedPlaces.features.forEach(function(place) {
            var p = projection(place.geometry.coordinates);
            context.fillText(place.properties.NAME, p[0], p[1] - 7);
        });
        context.restore();
    };

    updateProjection();
});

function updateProjection() {
    projection
        .distance(controls.select('#distance').node().value)
        .tilt(controls.select('#tilt').node().value)
        .scale(controls.select('#scale').node().value)
        .rotate([
            controls.select('#yaw').node().value,
            controls.select('#pitch').node().value,
            controls.select('#roll').node().value
        ])
        .center([
            controls.select('#cx').node().value,
            controls.select('#cy').node().value
        ])
        .clipAngle(Math.acos(1 / controls.select('#distance').node().value) * 180 / Math.PI - 1e-6);
    drawMap();
}

</script>
</body>
</html>

build.sh

#!/usr/bin/env bash


####
# Setup
mkdir -p tmp zip geojson

####
# Download data and convert to GeoJSON

# States
if [ ! -e zip/states.zip ]; then
    curl -L -o zip/states.zip 'http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip'
fi

if [ ! -e geojson/states.json ]; then
    unzip -o -d tmp zip/states.zip
    mapshaper tmp/ne_10m_admin_1_states_provinces.shp -o geojson/states.json
fi

# Populated places
if [ ! -e zip/populated-places.zip ]; then
    curl -L -o zip/populated-places.zip 'http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_populated_places.zip'
fi

if [ ! -e geojson/populated-places.json ]; then
    unzip -o -d tmp zip/populated-places.zip
    mapshaper tmp/ne_10m_populated_places.shp -o geojson/populated-places.json force
fi

# Urban areas
if [ ! -e zip/urban-areas.zip ]; then
    curl -L -o zip/urban-areas.zip 'http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_urban_areas.zip'
fi

if [ ! -e geojson/urban-areas.json ]; then
    unzip -o -d tmp zip/urban-areas.zip
    mapshaper tmp/ne_10m_urban_areas.shp -o geojson/urban-areas.json force
fi

####
# Combine data into TopoJSON\

mapshaper \
    -i  geojson/states.json \
        geojson/populated-places.json \
        geojson/urban-areas.json \
        combine-files \
    -filter 'ADM0NAME == "United States of America" && ADM1NAME !== "Alaska" && ADM1NAME !== "Hawaii" && SCALERANK < 4' target=populated-places \
    -filter 'admin == "United States of America"' target=states \
    -clip states target=urban-areas \
    -simplify 8% \
    -o topo.json format=topojson target=* force

####
# Clean up
rm -rf tmp