Map shading according to data for a set of coordinates

I have a set of 1000 coordinates, each of which represents urban centers of a certain state. These cities belong to one of the 20 counties, and I know which cities belong to the county. I want to shade a map based on the data that I have for each county. For example, if the graph has a value of 100%, then I want the color of this district to be dark red, if the county is associated with 0%, then I will color this part of the map with white.

I don’t have borders for each county, because they are old counties, and it will take a lot of work to keep track of borders from old maps. I have borders of the state, including islands and lakes, etc.

So here are the data that I have:

Town1 50.1,4.89 County1 Town2 49.9,4.78 County1 Town3 50.3,4.59 County1 Town4 50.2,4.99 County1 Town5 50.0,4.99 County1 ... Town1000 57.0,8.33 County20 

and

 County1 100% County2 100% County3 68% ... County20 0% 

as well as state boundaries.

Solution1: Thus, one of the ways to create my desired map can be to create polygons around each coordinate (city), and this polygon represents the entire area on the map closest to this city and closer to no other city. Then I draw this polygon according to its county.

Solution2: Perhaps the best approach would be a combination of colors between cities. Therefore, if I have two neighboring cities in different counties, one with 100% and one with 0%, then half the path between them will be pink (halfway between dark red and white).

Therefore, I want to programmatically create this map as an image file, where this file is easy to scale and where I can import it into Photoshop to add other elements. Would you recommend SVG in this case?

What library or algorithm can I use to create polygons, as required in solution 1?

Is there a library that I can use to create an SVG document with a mesh type gradient, as required in solution 2?

I want to use Python3 if possible, but I am open to other languages. I am also open to other SVG solutions and alternatives.

I am using macOS.

+5
source share
3 answers

Your first approach is called the Voronoi diagram.

See description on wikipedia

There is a solution for such diagrams using the D3 library for javascipt

approach D3

To make this decision complete, I insert the code from the example of M. Bostok here

 var w = 1280, h = 800; var projection = d3.geo.azimuthal() .mode("equidistant") .origin([-98, 38]) .scale(1400) .translate([640, 360]); var path = d3.geo.path() .projection(projection); var svg = d3.select("body").insert("svg:svg", "h2") .attr("width", w) .attr("height", h); var states = svg.append("svg:g") .attr("id", "states"); var cells = svg.append("svg:g") .attr("id", "cells"); d3.json("us-states.json", function(collection) { states.selectAll("path") .data(collection.features) .enter().append("svg:path") .attr("d", path); }); d3.csv("airports.csv", function(airports) { var positions = []; airports.forEach(function(airport) { positions.push(projection([+airport.longitude, +airport.latitude])); }); // Compute the Voronoi diagram of airports' projected positions. var polygons = d3.geom.voronoi(positions); var g = cells.selectAll("g") .data(airports) .enter().append("svg:g"); g.append("svg:path") .attr("class", "cell") .attr("d", function(d, i) { return "M" + polygons[i].join("L") + "Z"; }) .on("mouseover", function(d, i) { d3.select("#footer span").text(d.name); d3.select("#footer .hint").text(d.city + ", " + d.state); }); g.append("svg:circle") .attr("cx", function(d, i) { return positions[i][0]; }) .attr("cy", function(d, i) { return positions[i][1]; }) .attr("r", 1.5); }); 

Your second solution is easily achievable when shading OpenGL and Gouraud, but then it’s not easy to export it to SVG (or anything else for a bitmap). I will think of any alternative.

+1
source

For solution 1 in python, you can use Voronoi diagrams in scipy. The following code (a modified version of this SO post ) creates a voronoi diagram, draws it using different alpha values ​​according to the values ​​of each country, and saves it on a Map.png image:

 import matplotlib.pyplot as plt import numpy as np from scipy.spatial import Voronoi, voronoi_plot_2d import random # make up data points points = np.random.rand(15,2) values = np.random.uniform(low=0.0, high=1.0, size=(len(points),)) # compute Voronoi tesselation vor = Voronoi(points) # plot voronoi_plot_2d(vor) # colorize for region, value in zip(vor.regions, values): if not -1 in region: polygon = [vor.vertices[i] for i in region] plt.fill(*zip(*polygon), color='r', alpha=value) plt.savefig('Map.png') 

Your points should be loaded into the points variable on line 7 and the country percent in the values variable on line 8.

+1
source

Although powerful structures exist for presenting common data, GIS is the right tool for this particular task. I will try to explain how to do this in QGIS.

  • Prepare your data for import into QGIS.

Create a CSV file with your data in the following format:

 Town,X,Y,County,Value Town1,50.1,4.89,County1,100 Town2,49.9,4.78,County1,100 Town3,50.3,4.59,County1,100 Town4,50.2,4.99,County1,100 Town5,50.0,4.99,County1,100 ... Town1000,57.0,8.33,County20,0 
  1. Add it as a CSV layer (separator text)

    • Layer β†’ Add Layer β†’ Add Separate Text Layer
  2. Customize the style of the created layer to get a heat map, as you describe in your second option.

    • Right-click on the name of the layer in the layers panel (bottom left) β†’ Properties β†’ Symbology
    • Change the Single Character drop-down list to Heatmap.
    • Click Apply
    • Select the "Value" field (remember it in CSV?) In the "Weight Points" section
    • Apply
    • Set other parameters (color ramp, radius) for your needs.
  3. Create Voronoi polygons from this layer

    • Vector β†’ Geometry Tools β†’ Voronoi Polygons
    • Click "..." next to the input field for the path of the final layer, select "Save to file", save it at the GeoJSON level (QGIS does not support polygon layers in CSV, you can choose a different format if you understand).
    • Click Run In Background.
  4. Customize the style of the newly created layer to get areas of each county painted in different colors.

    • Right-click on the name of the layer in the layers panel (bottom left) β†’ Properties β†’ Symbology
    • Choose "Categorize" instead of "Single Character"
    • Select County from the Column drop-down list.
    • Click Classify to assign colors to all counties.
    • Apply
  5. To create an SVG from the current map:

    • Project β†’ New print layout

    • Click the "Add a new map to the layout" button on the toolbar, draw a large rectangle of the map to place it on the layout.

    • Click "Export as SVG"

What all.

Black and transparent heat map over Voronoi polygons - https://svgshare.com/s/5MZ .

Heatmap only - https://svgshare.com/s/5ND .

The script I used to generate the data is https://gist.github.com/ei-grad/1355223cd8a3c6ba16deb454ddef50b4 .

0
source

Source: https://habr.com/ru/post/1274695/


All Articles