user3331102 user3331102 - 4 months ago 99
JSON Question

topojson / D3 / Map with longitude + latitude

Im using the follwing UK Geo JSON to render a UK SVG Map
http://martinjc.github.io/UK-GeoJSON/json/eng/topo_eer.json

On this map i want to be able to take longitude + latitude points and plot onto the map.

I am adding a GeometryCollection place to the map in the following way:

data.objects.places = {
type: "GeometryCollection",
geometries: [
{
type: "Point",
coordinates: [-0.127758, 51.507351], // London
properties: {
name: "London - Testing"
}
}
]
};


However the coordinates are not in the correct place.

Below is the full javascript.

var width = 960;
var height = 1000;

var projection = d3.geo.albers()
.center([0, 55.4])
.rotate([4.4, 0])
.parallels([50, 60])
.scale(4000)
.translate([width / 2, height / 2]);

var path = d3.geo.path().projection(projection);

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

d3.json("topo_eer.json", function(error, data) {

// Create path for the UK
svg.selectAll(".subunit")
.data(topojson.feature(data, data.objects.eer).features)
.enter().append("path")
.attr("class", function(d) { return "subunit " + d.id; })
.attr("d", path);

// Path around regions
svg.append("path")
.datum(topojson.mesh(data, data.objects.eer, function(a, b) { return a !== b; }))
.attr("d", path)
.attr("class", "subunit-boundary");

// Add places to our data
data.objects.places = {
type: "GeometryCollection",
geometries: [
{
type: "Point",
coordinates: [-0.127758, 51.507351], // London
properties: {
name: "London - Testing"
}
}
]
};

// try plotting a point
svg.append("path")
.datum(topojson.feature(data, data.objects.places))
.attr("d", path)
.attr("class", "place-online");

console.log(data);

});

Answer

In a TopoJSON, those numbers in the coordinates are not the actual latitude/longitude values. They have to be transformed. This function transforms the quantized topology to absolute coordinates:

function transformPoint(topology, position) {
    position = position.slice();
    position[0] = position[0] * topology.transform.scale[0] 
    + topology.transform.translate[0],
    position[1] = position[1] * topology.transform.scale[1] 
    + topology.transform.translate[1]
    return position;
};

You'll find the scale and translate at the end of the TopoJSON you linked:

"transform":
    {"scale":
        [0.000818229038834542,0.0005946917122888551],
    "translate":[-6.418556211736409,49.8647494628352]
    }

Based on that function, I believe it's easy to write a function that does the reverse:

function transformPointReversed(topology, position) {
    position = position.slice();
    position[0] = (position[0] - topology.transform.translate[0])
    /(topology.transform.scale[0]),
    position[1] = (position[1] - topology.transform.translate[1])
    /(topology.transform.scale[1]) 
    return position;
};

I tried this function I just made and your London coordinates returned me this array:

[7688.309645789168, 2762.1059840278253]

Please, test it in your coordinates to see if it works.

An alternative is overlaying your TopoJSON with a GeoJSON, which does use an absolute coordinates system.

Here is the API reference: https://github.com/mbostock/topojson-specification/blob/master/README.md#22-geometry-objects

Comments