Converts shaded relief .tif to .png and uses that for texture underneath a vector layer.
Sources
<!DOCTYPE html>
<html>
<head>
<title>Shaded relief</title>
<meta charset="utf-8">
<style>
body {
padding: 0;
margin: 0;
}
.parishes {
fill: none;
stroke: white;
stroke-width: 1px;
stroke-opacity: 1;
}
.raster {
fill: none;
opacity: 1;
}
.country-border {
fill: none;
stroke: red;
stroke-width: 5px;
stroke-opacity: 0.7;
}
</style>
</head>
<body>
<svg id="map"></svg>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/queue.v1.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var map_width = 600,
map_height = 600;
var svg = d3.select("#map")
.attr("width", map_width)
.attr("height", map_height);
var la_vector_projection = d3.geo.mercator()
.scale(1)
.translate([0, 0]);
var la_vector_path = d3.geo.path()
.projection(la_vector_projection);
queue()
.defer(d3.json, "louisiana.json")
.await(ready);
function ready(error, louisiana) {
if (error) throw error;
var parishes = topojson.feature(louisiana, louisiana.objects.louisiana);
var b = la_vector_path.bounds(parishes),
s = 1 / Math.max((b[1][0] - b[0][0]) / map_width, (b[1][1] - b[0][1]) / map_height),
t = [(map_width - s * (b[1][0] + b[0][0])) / 2, (map_height - s * (b[1][1] + b[0][1])) / 2];
// Update the projection to use computed scale & translate.
la_vector_projection
.scale(s)
.translate(t);
// Raster
var raster_width = (b[1][0] - b[0][0]) * s;
var raster_height = (b[1][1] - b[0][1]) * s;
var rtranslate_x = (map_width - raster_width) / 2;
var rtranslate_y = (map_height - raster_height) / 2;
// Shaded relief, Louisiana
// svg.append("clipPath")
// .attr("id", "la_clip")
// .append("use")
// .attr("xlink:href", "#louisiana");
svg.append("image")
.attr("clip-path", "url(#la_clip)")
.attr("xlink:href", "louisiana-crop.png")
.attr("class", "raster")
.attr("width", raster_width)
.attr("height", raster_height)
.attr("transform", "translate(" + rtranslate_x + ", " + rtranslate_y + ")");
// svg.append("use")
// .attr("xlink:href", "#louisiana");
// Draw parishes
svg.append("path")
.datum(parishes)
.datum(parishes, function(a, b) { return a !== b; })
.attr("class", "parishes")
.attr("id", "louisiana") // For shaded relief
.attr("d", la_vector_path);
}
// Allows iframe on bl.ocks.org.
d3.select(self.frameElement).style("height", map_height + "px");
</script>
</body>
</html>
.PHONY: all clean
.SECONDARY:
# Download 90-meter SRTM tiles for Louisiana
zip/srtm_%.zip:
@mkdir -p $(dir $@)
@curl -sS -o $@.download 'http://srtm.csi.cgiar.org/SRT-ZIP/SRTM_V41/SRTM_Data_GeoTiff/$(notdir $@)'
@mv $@.download $@
zip/tl_2015_us_county.zip:
@mkdir -p $(dir $@)
@curl -sS -o $@.download 'ftp://ftp2.census.gov/geo/tiger/TIGER2015/COUNTY/$(notdir $@)'
@mv $@.download $@
# Unzip
tif/srtm_%.tif: zip/srtm_%.zip
@mkdir -p $(dir $@)
@rm -rf tmp && mkdir tmp
@unzip -q -o -d tmp $<
@cp tmp/* $(dir $@)
@rm -rf tmp
shp/tl_2015_us_county.shp: zip/tl_2015_us_county.zip
@mkdir -p $(dir $@)
@rm -rf tmp && mkdir tmp
@unzip -q -o -d tmp $<
@cp tmp/* $(dir $@)
@rm -rf tmp
# Extract Louisiana from U.S.
shp/louisiana.shp: shp/tl_2015_us_county.shp
@mkdir -p $(dir $@)
@ogr2ogr \
-f 'ESRI Shapefile' \
-t_srs "EPSG:4326" \
$@ $< \
-dialect sqlite \
-sql "SELECT Geometry, \
STATEFP \
FROM tl_2015_us_county \
WHERE STATEFP = '22'"
# Convert to GeoJSON
geojson/louisiana.json: shp/louisiana.shp
@mkdir -p $(dir $@)
@ogr2ogr \
-f 'GeoJSON' \
$@ $<
# Convert to TopoJSON
topojson/louisiana.json: geojson/louisiana.json
@mkdir -p $(dir $@)
@topojson \
--properties \
--no-quantization \
-o $@ \
-- $<
# Simplify TopoJSON
louisiana.json: topojson/louisiana.json
@mkdir -p $(dir $@)
@topojson \
--properties \
--spherical \
-q 1e8 \
-s 1e-10 \
-o $@ \
-- $<
# Merge tiles
tif/louisiana-merged.tif: \
tif/srtm_17_07.tif \
tif/srtm_18_07.tif \
tif/srtm_19_07.tif \
tif/srtm_17_06.tif \
tif/srtm_18_06.tif \
tif/srtm_19_06.tif \
tif/srtm_17_05.tif \
tif/srtm_18_05.tif \
tif/srtm_19_05.tif
@mkdir -p $(dir $@)
@gdal_merge.py \
-o $@ \
-init "255" \
tif/srtm_*.tif
# Convert to Mercator, from WGS 84
tif/louisiana-reprojected.tif: tif/louisiana-merged.tif
@mkdir -p $(dir $@)
@gdalwarp \
-co "TFW=YES" \
-s_srs "EPSG:4326" \
-t_srs "EPSG:3857" \
$< \
$@
# Crop to Louisiana shape
tif/louisiana-cropped.tif: tif/louisiana-reprojected.tif shp/louisiana.shp
@mkdir -p $(dir $@)
@# Use nodata=255 to ignore white.
@gdalwarp \
-cutline shp/louisiana.shp \
-crop_to_cutline \
-dstalpha \
-srcnodata 255 \
-dstnodata 255 \
tif/louisiana-reprojected.tif $@
tif/louisiana-color-crop.tif: tif/louisiana-cropped.tif
@rm -rf tmp && mkdir -p tmp
@gdaldem \
hillshade \
$< tmp/hillshade.tmp.tif \
-z 5 \
-az 315 \
-alt 60 \
-compute_edges
@gdal_calc.py \
-A tmp/hillshade.tmp.tif \
--outfile=$@ \
--calc="255*(A>220) + A*(A<=220)"
@gdal_calc.py \
-A tmp/hillshade.tmp.tif \
--outfile=tmp/opacity_crop.tmp.tif \
--calc="1*(A>220) + (256-A)*(A<=220)"
@rm -rf tmp
tif/louisiana-color.tif: tif/louisiana-reprojected.tif
@rm -rf tmp && mkdir -p tmp
@gdaldem \
hillshade \
$< tmp/hillshade.tmp.tif \
-z 5 \
-az 315 \
-alt 60 \
-compute_edges
@gdal_calc.py \
-A tmp/hillshade.tmp.tif \
--outfile=$@ \
--calc="255*(A>220) + A*(A<=220)"
@gdal_calc.py \
-A tmp/hillshade.tmp.tif \
--outfile=tmp/opacity_crop.tmp.tif \
--calc="1*(A>220) + (256-A)*(A<=220)"
@rm -rf tmp
louisiana-crop.png: tif/louisiana-color-crop.tif
@convert \
-resize x670 \
$< $@
louisiana.png: tif/louisiana-color.tif
@convert \
-resize x670 \
$< $@
clean:
@rm -f *.json louisiana*.png
@rm -rf geojson
@rm -rf shp
@rm -rf tif
@rm -rf topojson
all: louisiana.png \
louisiana-crop.png \
louisiana.json