A responsive (and label-less) version of Laris Karklis's Ukraine's language divide map using D3.
Original language data from the Ukraine Census. Spatial data from Natural Earth.
A responsive (and label-less) version of Laris Karklis's Ukraine's language divide map using D3.
Original language data from the Ukraine Census. Spatial data from Natural Earth.
| <!DOCTYPE html> | |
| <meta charset='utf-8'> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, minimum-scale=1.0, maximum-scale=1.0"> | |
| <style> | |
| body { | |
| width: 100%; | |
| max-width: 600px; | |
| margin: auto; | |
| } | |
| .country { | |
| fill: #E7E7E7; | |
| stroke: none; | |
| } | |
| .country-boundary { | |
| fill: none; | |
| stroke: #999; | |
| } | |
| .region { | |
| stroke: none; | |
| } | |
| .region-boundary { | |
| fill: none; | |
| stroke: #424242; | |
| stroke-dasharray: 2 2; | |
| stroke-linejoin: round; | |
| opacity: 0.5; | |
| } | |
| .ukraine-boundary { | |
| fill: none; | |
| stroke: #333; | |
| } | |
| .river { | |
| fill: none; | |
| stroke: #fff; | |
| } | |
| .lake { | |
| fill: #fff; | |
| stroke: none; | |
| } | |
| </style> | |
| <body> | |
| <div id='map'></div> | |
| <script src='http://d3js.org/d3.v3.min.js'></script> | |
| <script src='http://d3js.org/topojson.v1.min.js'></script> | |
| <script> | |
| var mapScale = 4.35; | |
| var mapRatio = .65; | |
| var width = parseInt(d3.select('#map').style('width')); | |
| var height = width * mapRatio; | |
| var projection = d3.geo.albers() | |
| .center([0, 48.5]) | |
| .rotate([-31.5, 0]) | |
| .parallels([45, 50]) | |
| .scale(width * mapScale) | |
| .translate([width / 2, height / 2]); | |
| var color = d3.scale.threshold() | |
| .domain([10, 20, 30, 50]) | |
| .range(['#F7CB6D', '#C4AA73', '#899BAD', '#739AC4', '#2E5C8A']) | |
| var svg = d3.select('#map').append('svg') | |
| .attr('width', width) | |
| .attr('height', height); | |
| var countriesPath; | |
| var regionsPath; | |
| var riversPath; | |
| var lakesPath; | |
| d3.json('ukraine.json', function(error, data) { | |
| var countries = topojson.feature(data, data.objects.countries); | |
| countriesPath = d3.geo.path() | |
| .projection(projection); | |
| svg.selectAll('.country') | |
| .data(countries.features) | |
| .enter().append('path') | |
| .attr('class', 'country') | |
| .attr('d', countriesPath) | |
| var countryBoundaries = topojson.mesh(data, data.objects.countries, | |
| function(a, b) { return a !== b }); | |
| svg.append('path') | |
| .datum(countryBoundaries) | |
| .attr('class', 'country-boundary') | |
| .attr('d', countriesPath) | |
| var regions = topojson.feature(data, data.objects['ukraine-regions']); | |
| regionsPath = d3.geo.path() | |
| .projection(projection); | |
| svg.selectAll('.region') | |
| .data(regions.features) | |
| .enter().append('path') | |
| .attr('class', 'region') | |
| .attr('d', regionsPath) | |
| .style('fill', function(d) { return color(d.properties.percent); }) | |
| var rivers = topojson.feature(data, data.objects['rivers_lake_centerlines']); | |
| riversPath = d3.geo.path() | |
| .projection(projection); | |
| svg.selectAll('.river') | |
| .data(rivers.features) | |
| .enter().append('path') | |
| .attr('class', 'river') | |
| .attr('d', riversPath) | |
| var lakes = topojson.feature(data, data.objects.lakes); | |
| lakesPath = d3.geo.path() | |
| .projection(projection); | |
| svg.selectAll('.lake') | |
| .data(lakes.features) | |
| .enter().append('path') | |
| .attr('class', 'lake') | |
| .attr('d', lakesPath) | |
| var ukraineRegionBoundaries = topojson.mesh(data, | |
| data.objects['ukraine-regions'], function(a, b) { return a !== b }); | |
| svg.append('path') | |
| .datum(ukraineRegionBoundaries) | |
| .attr('d', regionsPath) | |
| .attr('class', 'region-boundary') | |
| var ukraineBoundaries = topojson.mesh(data, | |
| data.objects['ukraine-regions'], function(a, b) { return a === b }); | |
| svg.append('path') | |
| .datum(ukraineBoundaries) | |
| .attr('d', regionsPath) | |
| .attr('class', 'ukraine-boundary') | |
| d3.select(window).on('resize', resize); | |
| }); | |
| function resize() { | |
| width = parseInt(d3.select('#map').style('width')); | |
| height = width * mapRatio; | |
| svg | |
| .style('width', width + 'px') | |
| .style('height', height + 'px'); | |
| svg.selectAll('.country,.country-boundary').attr('d', countriesPath); | |
| svg.selectAll('.region,.region-boundary,.ukraine-boundary').attr('d', regionsPath); | |
| svg.selectAll('.lake').attr('d', lakesPath); | |
| svg.selectAll('.river').attr('d', riversPath); | |
| projection | |
| .scale(width * mapScale) | |
| .translate([width / 2, height / 2]); | |
| } | |
| </script> |
| GENERATED_FILES = \ | |
| build/ne_10m_admin_1_states_provinces.shp \ | |
| build/ne_10m_admin_0_countries.shp \ | |
| public/ukraine.json | |
| LIBRARY_FILES = \ | |
| public/lib/d3.v3.min.js \ | |
| public/lib/topojson.v1.min.js \ | |
| BOUNDS = 19.0 43.2 42.3 53.3 | |
| all: $(GENERATED_FILES) $(LIBRARY_FILES) | |
| clean: | |
| rm -rf build | |
| rm -rf $(GENERATED_FILES) $(LIBRARY_FILES) | |
| build/ne_10m_admin_1_states_provinces.zip: | |
| mkdir -p build | |
| curl -o $@.download "http://www.nacis.org/naturalearth/10m/cultural/ne_10m_admin_1_states_provinces.zip" | |
| mv $@.download $@ | |
| build/ne_10m_admin_1_states_provinces.shp: build/ne_10m_admin_1_states_provinces.zip | |
| unzip -d build $< | |
| touch build/ne_10m_admin_1_states_provinces.* | |
| build/ne_10m_admin_0_countries.zip: | |
| mkdir -p build | |
| curl -o $@.download "http://www.nacis.org/naturalearth/10m/cultural/ne_10m_admin_0_countries.zip" | |
| mv $@.download $@ | |
| build/ne_10m_admin_0_countries.shp: build/ne_10m_admin_0_countries.zip | |
| unzip -d build $< | |
| touch build/ne_10m_admin_0_countries.* | |
| build/ne_10m_lakes.zip: | |
| mkdir -p build | |
| curl -o $@.download "http://www.nacis.org/naturalearth/10m/physical/ne_10m_lakes.zip" | |
| mv $@.download $@ | |
| build/ne_10m_rivers_lake_centerlines.zip: | |
| mkdir -p build | |
| curl -o $@.download "http://www.nacis.org/naturalearth/10m/physical/ne_10m_rivers_lake_centerlines.zip" | |
| mv $@.download $@ | |
| build/ne_10m_lakes.shp: build/ne_10m_lakes.zip | |
| unzip -d build $< | |
| touch build/ne_10m_lakes.* | |
| build/ne_10m_rivers_lake_centerlines.shp: build/ne_10m_rivers_lake_centerlines.zip | |
| unzip -d build $< | |
| touch build/ne_10m_rivers_lake_centerlines.* | |
| build/ukraine-regions.json: build/ne_10m_admin_1_states_provinces.shp | |
| mkdir -p build | |
| rm -f $@ | |
| ogr2ogr -f GeoJSON -where "ADM0_A3 IN ('UKR')" -clipsrc $(BOUNDS) $@ $< | |
| build/countries.json: build/ne_10m_admin_0_countries.shp | |
| mkdir -p build | |
| rm -f $@ | |
| ogr2ogr -f GeoJSON -clipsrc $(BOUNDS) $@ $< | |
| build/lakes.json: build/ne_10m_lakes.shp | |
| mkdir -p build | |
| rm -f $@ | |
| ogr2ogr -f GeoJSON -clipsrc $(BOUNDS) $@ $< | |
| build/rivers_lake_centerlines.json: build/ne_10m_rivers_lake_centerlines.shp | |
| mkdir -p build | |
| rm -f $@ | |
| ogr2ogr -f GeoJSON -clipsrc $(BOUNDS) $@ $< | |
| public/ukraine.json: build/ukraine-regions.json build/countries.json build/lakes.json build/rivers_lake_centerlines.json data/regions-speaking-russian.csv | |
| topojson -o $@ -q 1e4 -e data/regions-speaking-russian.csv \ | |
| --id-property adm1_code -p +percent,name \ | |
| -- build/ukraine-regions.json build/countries.json build/lakes.json \ | |
| build/rivers_lake_centerlines.json | |
| public/lib/d3.v3.min.js: | |
| mkdir -p public/lib | |
| curl -o $@ "http://d3js.org/d3.v3.min.js" | |
| public/lib/topojson.v1.min.js: | |
| mkdir -p public/lib | |
| curl -o $@ "http://d3js.org/topojson.v1.min.js" |
| adm1_code | Region | percent | |
|---|---|---|---|
| Ukraina | 29.59 | ||
| UKR-283 | Avtonomna Respublika Krym | 76.55 | |
| UKR-323 | Vinnytska oblast | 4.74 | |
| UKR-318 | Volynska oblast | 2.51 | |
| UKR-326 | Dnipropetrovska oblast | 31.91 | |
| UKR-327 | Donetska oblast | 74.92 | |
| UKR-324 | Zhytomyrska oblast | 6.57 | |
| UKR-293 | Zakarpatska oblast | 2.90 | |
| UKR-331 | Zaporizka oblast | 48.19 | |
| UKR-289 | Ivano-Frankivska oblast | 1.78 | |
| UKR-321 | Kyivska oblast | 7.17 | |
| UKR-320 | Kirovohradska oblast | 10.02 | |
| UKR-329 | Luhanska oblast | 68.84 | |
| UKR-291 | Lvivska oblast | 3.77 | |
| UKR-284 | Mykolaivska oblast | 29.26 | |
| UKR-322 | Odeska oblast | 41.95 | |
| UKR-330 | Poltavska oblast | 9.47 | |
| UKR-286 | Rivnenska oblast | 2.73 | |
| UKR-325 | Sumska oblast | 15.50 | |
| UKR-292 | Ternopilska oblast | 1.19 | |
| UKR-328 | Kharkivska oblast | 44.29 | |
| UKR-4827 | Khersonska oblast | 24.86 | |
| UKR-290 | Khmelnytska oblast | 4.09 | |
| UKR-319 | Cherkaska oblast | 6.66 | |
| UKR-288 | Chernivetska oblast | 5.27 | |
| UKR-285 | Chernihivska oblast | 10.26 | |
| UKR-4826 | m. Kyiv | 25.27 | |
| UKR-5482 | m. Sevastopol | 90.56 |