I have an image of which I know the lower left and upper right coordinates of the mercator projection (lat/long).
I added it to Google Maps as a google.maps.GroundOverlay
, which gives me the correct result
I've added some color markers on the top-left (green), top-right (red), bottom-right (cyan) and bottom-left (red) corners of the image.
This is all fine, except that I want to move from Google Maps over to d3 with topojson because d3 is a lot more lightweight and Google Map's features are not required.
I've created the d3-based map, including the markers of the corners of the image, this is the result:
If I overlay the two windows (the browser which contains the Google Maps map and the browser which contains the d3 map), then I can get the country boundries and corner points to overlap very precisely.
Here is an animated gif which has the d3-map browser over the Google Maps map browser, and the tranparency of the d3-map browser window is being altered with the help of a tool. It changes from fully opaque to fully transparent, then to fully opaque again. The image which shows the country names is the Google Maps one.
As you can see, the points and country borders align very nicely, but the radar-images don't. Moving the d3 image down a bit eases the problem, but is not the correct solution. I know that the one shown by Google Maps is.
What I'm doing: in the d3 map I'm creating the projection
projection = d3.geo.mercator().scale(scale).translate([width / 2, width / 2]).center(center);
Then creating a bounding box of the provided image latlong corners.
bb = {
east: 16.0,
west: 4.0,
north: 56.0,
south: 46.0,
}
And project them from the latlong-space into canvas x,y coordinates.
p_ur = projection([bb.east, bb.north]).map(function(x) { return x; })
p_ll = projection([bb.west, bb.south]).map(function(x) { return x; })
p_ul = projection([bb.west, bb.north]).map(function(x) { return x; })
p_lr = projection([bb.east, bb.south]).map(function(x) { return x; })
When I draw the circles, the positions match the ones on Google Maps very precisely. This means that the projection is working.
circle = svg.append("svg:circle").attr('cx',p_ll[0]).attr('cy',p_ll[1]).attr('r', 2).attr("class", "ll")
circle = svg.append("svg:circle").attr('cx',p_ur[0]).attr('cy',p_ur[1]).attr('r', 2).attr("class", "ur")
circle = svg.append("svg:circle").attr('cx',p_ul[0]).attr('cy',p_ul[1]).attr('r', 2).attr("class", "ul")
circle = svg.append("svg:circle").attr('cx',p_lr[0]).attr('cy',p_lr[1]).attr('r', 2).attr("class", "lr")
Before drawing the circles, I'm drawing the topojson country borders
path = d3.geo.path().projection(projection);
countries = svg.append("g");
// ...fetching the data from the web...
countries.selectAll('.country')
.data(topojson.feature(data, data.objects.europe).features)
.enter()
.append('path')
.attr('class', 'country')
.attr('d', path);
And here comes the problem: I'm applying the image in a very "el cheapo" way. I project the upper-left latlong into the canvas x,y space, and use that as the x,y origin for the svg:image
. These points already got computed for the color circles above, so I'm re-using them here.
image = svg.append("svg:image")
.attr('x', p_ul[0])
.attr('y', p_ul[1])
.attr('width', (p_lr[0] - p_ul[0]))
.attr('height', (p_lr[1] - p_ul[1]))
.attr('preserveAspectRatio', 'none')
.attr("xlink:href","http://.../current.png")
Somehow I'm skipping the projection transformation of the image's individual pixel data by just using the corners to pin the image on top of the map.
I asume that I should treat each x,y position of the image as a lat/long position, and then project each pixel through a mercator projection onto the map.
But I've got no clue on how to do it. Any ideas? Is there a simple overlay plug-in for d3.geo akin to google.maps.GroundOverlay
?