D3.Js Drawing Geojson Incorrectly

D3.js Drawing geojson incorrectly

The issue is the winding order of the coordinates (see this block). Most tools/utilities/libraries/validators don't really care about winding order because they treat geoJSON as containing Cartesian coordinates. Not so with D3 - D3 uses ellipsoidal math - benefits of this is include being able to cross the antimeridian easily and being able to select an inverted polygon.

The consequence of using ellipsoidal coordinates is the wrong winding order will create a feature of everything on the planet that is not your target (inverted polygon). Your polygons actually contain a combination of both winding orders. You can see this by inspecting the svg paths:

Sample Image

Here one path appears to be accurately drawn, while another path on top of it covers the entire planet - except for the portion it is supposed to (the space it is supposed to occupy covered by other paths that cover the whole world).

This can be simple to fix - you just need to reorder the coordinates - but as you have features that contain both windings in the same collection, it'll be easier to use a library such as turf.js to create a new array of properly wound features:

    var fixed = features.map(function(feature) {
return turf.rewind(feature,{reverse:true});
})

Note the reverse winding order - through an odd quirk, D3, which is probably the most widespread platform where winding order matters actually doesn't follow the geoJSON spec (RFC 7946) on winding order, it uses the opposite winding order, see this comment by Mike Bostock:

I’m disappointed that RFC 7946 standardizes the opposite winding order
to D3, Shapefiles and PostGIS. And I don’t see an easy way for D3 to
change its behavior, since it would break all existing (spherical)
GeoJSON used by D3. (source)

By rewinding each polygon we get a slightly more useful map:

Sample Image

An improvement, but the features are a bit small with these projection settings.

By adding a fitSize method to scale and translate we get a much better looking map (see block here):

Sample Image

d3.js not rendering geojson data correctly

In the SELECT statement when getting the data from SQL Server, I added ReorientObject() to the clause like the following. Then my shapes appeared properly.

SELECT Location.ReorientObject().STAsText() AS WKT FROM MyTable

Why is this geojson not rendering with D3?

Your coordinates are unprojected, a collection of latitude longitude pairs, but you are treating these coordinates as pixel coordinates. This shows with your resulting path data: M-85.388717,33.913044, a coordinate representing -85° E, 34° N. That the x value, longitude here, is negative means your map is rendered outside the SVG - to its left, hence why it is not visible.

You need to run your latitute longitute pairs through a geographic projection that takes those points on a 3D sphere and projects them to the 2D plane of your SVG. In doing so you'll introduce some degree of distortion, so you may want to try a few projections (though this is often minimal if zoomed in enough, say on a city).

If you do not provide a projection to d3.geoPath() it'll use a null projection which treats input coordinates as pixel coordinates. This won't let you simply shift the map to the right: geographic convention is that latitutes increase as one moves north (typically up on a map), whereas SVG/Canvas convention is that pixel y values increase as one moves down

For example to use an Albers projection you could use:

 var projection = d3.geoAlbers();
var path = d3.geoPath().projection(projection);

This gets us half way there: the projection is independent of your features, we need to center and scale the projection appropriately. To do so, after we load the geojson, we can use:

 projection.fitSize([width,height],data)

Where data is a geojson object (hence why we don't use data.features).

D3.js - Drawing points on map fails due to wrong projection

First of all, when searching for details taken from your redCrossLocations it appears to be downloaded from this source. Inspecting the full file reveals two issues with your geo input data:

  1. Your JSON seems to be Esri's JSON format, wheras D3 expects GeoJSON as its input. Because your JSON is not compatible with GeoJSON you will have to convert it.

  2. The file has a definition of "spatialReference":{"wkid":31258,"latestWkid":31258} which refers to the projected coordinate system MGI_Austria_GK_M31 (EPSG::31258) which is also not the right one to use when it comes to D3. To make it usable for D3 you need to transform it to the geographic coodinate system WGS 84 (EPSG::4326).

Fortunately, you can use the ogr2ogr tool to do both conversions in a single run:

ogr2ogr -f "GeoJSON" -t_srs "EPSG::4326" "RotesKreuz_Dienststellen.json"

Additionaly, there is an option -s_srs available, to explicitely specify the source coordinate system (-s_srs "EPSG:31258") which is not needed in your case, because this will be derived from the input file.

There is also a ogr2ogr web client available to do the conversion and transformation online. Using that form you may upload your file and specify the "Target SRS" to be EPSG:4326 which will transform the coordinate system and convert it to GeoJSON.

D3 V6 and JavaScript - GeoJSON fill is spilling outside of path

One or more of your GeoJSON entries are the wrong way around. The values are correct, but they are in the wrong order. d3-geo generally expects GeoJSON features to be clockwise:

Spherical polygons also require a winding order convention to determine which side of the polygon is the inside: the exterior ring for polygons smaller than a hemisphere must be clockwise, while the exterior ring for polygons larger than a hemisphere must be anticlockwise.

You can fix the winding of your data using a plugin or tool like turf, which you can use to "rewind" your shapes - though you should use the reverse: true option.

D3 - Large GeoJSON File does not show draw map properly using projections

I had a look at your problem. The problem seems to be with the map. I ran into the same problems as you have, although I had no issue creating a map from the older (pre 2008) files on the site you linked to. Mapshaper (mapshaper.org) had no problem plotting both graphs, so the problem seems to be with d3 and this specific map. I have no time to look into the reason for this.

Simplifying the map using mapshaper (which is something you might want to do anyway) seems to result in a map that can be correctly drawn:

ogr2ogr -f GeoJSON map_tmp.json spd_beats_wgs84.kmz
mapshaper -p 0.5 --repair -o map.json map_tmp.json

I can then draw the map using the following code:

var width = 800;
var height = 500;

var vis = d3.select("#vis").append("svg")
.attr("width", width).attr("height", height);

d3.json("map.json", function(map) {
var projection = d3.geo.mercator().scale(1).translate([0,0]).precision(0);
var path = d3.geo.path().projection(projection);
var bounds = path.bounds(map);

var scale = .95 / Math.max((bounds[1][0] - bounds[0][0]) / width,
(bounds[1][1] - bounds[0][1]) / height);
var transl = [(width - scale * (bounds[1][0] + bounds[0][0])) / 2,
(height - scale * (bounds[1][1] + bounds[0][1])) / 2];
projection.scale(scale).translate(transl);

vis.selectAll("path").data(map.features).enter().append("path")
.attr("d", path)
.style("fill", "none")
.style("stroke", "black");
});

Resulting in:

Sample Image

D3 polygon projection is wrong

The outer circle indicates that you have an inverted polygon: you are drawing a feature of the world minus the intended feature. As d3 uses spherical math in calculating projections, winding order matters, as opposed to most geographic tools which treat spherical coordinates as Cartesian (even when projecting). The first map below in red shows this by applying a fill.

The missing point is a bit odd, normally D3 won't render invalid geojson syntax and it won't throw an error or warning in not rendering anything. The issue here is that the last point in your coordinate array should be the first coordinate. I've forgotten where in the spec this is, and haven't looked as to why D3 renders it like this at all. When attempting to take a look at your geojson at geojson.io I noticed it didn't render at all with the missing end point.

I've rewound the coordinates (lazily with .reverse()) and added the extra point in the map on the right.

let hex = {"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[6.732,6],[5,7],[3.268,6],[3.268,4],[5,3],[6.732,4]]]}]};

let hex2 = {"type":"GeometryCollection","geometries":[{"type":"Polygon","coordinates":[[[6.732,6],[5,7],[3.268,6],[3.268,4],[5,3],[6.732,4],[6.732,6]].reverse()]}]};



let projection = d3.geoOrthographic().scale(125).translate([125,125]);
let path = d3.geoPath(projection);

let svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 250);

svg
.append("path")
.datum(hex)
.attr("d", path)
.attr("fill", "crimson");

svg.append("g")
.attr("transform","translate(250,0)")
.append("path")
.datum(hex2)
.attr("d", path)
.attr("fill","steelblue");




<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

D3.js svg does not draw Map

@AndrewReid was correct. It was a winding problem and fortunately there is a simple way to fix it using turf.js

d3.json(path).then(function (json) {
var projection = d3.geoMercator();
var features = json.features;

var fixed = features.map(function (feature) {
return turf.rewind(feature, { reverse: true });
})
//Projections
var geoPath = d3.geoPath().projection(projection);

projection.fitSize([width, height], { "type": "FeatureCollection", "features": fixed })

svg.selectAll("path")
.data(fixed)
.enter()
.append("path")
.attr("d", geoPath)


Related Topics



Leave a reply



Submit