Skip to content

Instantly share code, notes, and snippets.

@mourner
Created September 29, 2017 13:31
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mourner/14f52d4764299a8fcc204d699abdcc5b to your computer and use it in GitHub Desktop.
Save mourner/14f52d4764299a8fcc204d699abdcc5b to your computer and use it in GitHub Desktop.
Quick example of generating GeoJSON contours from raster with d3
'use strict';
var contours = require('d3-contour').contours;
var PNG = require('pngjs').PNG;
var fs = require('fs');
var png = PNG.sync.read(fs.readFileSync('./rain.png'));
var data = [];
for (var i = 0; i < png.height; i++) {
for (var j = 0; j < png.width; j++) {
data[i * png.width + j] = png.data[4 * (i * png.width + j) + 0];
}
}
var zValues = [8, 18, 36, 54, 72, 90, 108, 126, 144, 162, 180, 198, 216, 234, 252];
var polygons = contours()
.size([png.width, png.height])
.thresholds(zValues)
(data);
var geojson = {
type: 'FeatureCollection',
features: []
};
for (let polygon of polygons) {
if (polygon.coordinates.length === 0) continue;
let coords = convertCoords(polygon.coordinates);
geojson.features.push({
type: 'Feature',
properties: {
value: polygon.value
},
geometry: {
type: polygon.type,
coordinates: coords
}
})
}
function convertCoords(coords) {
var minLng = 63.8148899733;
var maxLng = 143.536486117;
var maxLat = 56.3833398551;
var minLat = 12.7700338517;
var result = [];
for (let poly of coords) {
var newPoly = [];
for (let ring of poly) {
if (ring.length < 4) continue;
var newRing = [];
for (let p of ring) {
newRing.push([
minLng + (maxLng - minLng) * (p[0] / png.width),
maxLat - (maxLat - minLat) * (p[1] / png.height)
]);
}
newPoly.push(newRing);
}
result.push(newPoly);
}
return result;
}
console.log(JSON.stringify(geojson));
@bentaber
Copy link

👍 👍 👍

@agdhruv
Copy link

agdhruv commented Apr 21, 2021

Thanks for this @mourner, it's very helpful! Love your blog posts and articles.

Few questions:

  1. What is the need for line 31: let coords = convertCoords(polygon.coordinates)?
  2. What other ways are available to convert grid data (raster) to GeoJSON? Are there blogs/tutorials which cover these ways?
  3. Some of these contour GeoJSONs may be pretty big in size. What method would you suggest to display them on an interactive Mapbox-based application?
    • I cannot upload them as a Mapbox Studio tileset as I have too many geojsons -- one for each hour since 2015, and counting.
    • If I have to host my own tileset, how do I split my GeoJSON into zoom/x/y/ directory structure? Tippecanoe seems to only convert into a .mbtiles file. This is still a single file whereas I want to fetch vector data as is required when the user pans or zooms.

@bentaber
Copy link

@agdhruv - hopefully this will help

  1. line 31: Need to convert the coordinates from lng lat geo coordinates into pixel based x,y coordinates to match up with the dimensions of the source raster image.
  2. Check out gdal_translate and gdal_polygonize and gdal_contour. From my experience you have much more control using d3-contour.
  3. Convert to geojson (or geobuf) as an intermediate step, then use https://github.com/mapbox/tippecanoe to convert into vector tilesets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment