4

I have an image of which I know the lower left and upper right coordinates of the mercator projection (lat/long).

image of current weather conditions in Germany

I added it to Google Maps as a google.maps.GroundOverlay, which gives me the correct result

enter image description here

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:

enter image description here

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.

enter image description here

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?

Daniel F
  • 13,684
  • 11
  • 87
  • 116
  • 1
    Sure would be nice if there were more geo plugins for D3, seems like having something like google maps GroundOverlay is a pretty common requirement – chrismarx Apr 04 '19 at 15:10

1 Answers1

2

Google Maps uses WEB MERCATOR projection, which varies slightly compared to true mercator projection (used by D3). (Lifted from Wikipedia: Web Mercator is a slight variant of the Mercator projection that is used primarily in Web-based mapping programs. It uses the same formulas as the standard Mercator as used for small-scale maps. However, the Web Mercator uses the spherical formulas at all scales whereas large-scale Mercator maps normally use the ellipsoidal form of the projection. The discrepancy is imperceptible at the global scale but causes maps of local areas to deviate slightly from true ellipsoidal Mercator maps at the same scale. This deviation becomes more pronounced further from the equator, and can reach as high as 35 km on the ground.

While the Web Mercator's formulas are for the spherical form of the Mercator, geographical coordinates are required to be in the WGS 84 ellipsoidal datum. This discrepancy causes the projection to be slightly non-conformal.

There is a S.O. discussion where the question was asked why the scaling changes when one includes raster images with D3. Again it was due to the differences between D3 and mercator. If your raster image is aligned perfectly in google maps... it should NEVER align in d3, due to the subtle variations between web mercator and mercator, and for it to work I would think that the raster image must be (reprojected) , and that is the reason you are seeing it misalign on the d3 vs the google maps. (editing to note that I used the wrong word. not scaled, rather REPROJECTED....)

Hope this helps a bit.

Actually some nice reprojecting software that is also open source can be found here: (part of the GDAL project). (hope that helps you a bit.)

Community
  • 1
  • 1
e3495026
  • 79
  • 1
  • 7
  • Thanks, this is interesting information. If I should get back to fixing my code, I will take this into account. So, apparently there are two problems. 1 is the per-pixel mapping and the other one the projection type. But I wonder why the corners match so well, as if the projection was ok. – Daniel F Nov 06 '16 at 20:09
  • The end points work... it is the middle that is all odd.... A nice (free) program that does reprojection FROM equilinier is here http://www.giss.nasa.gov/tools/gprojector/ – e3495026 Dec 04 '16 at 03:29
  • (some kool examples are from the nasa page of different projections..... – e3495026 Dec 04 '16 at 03:32
  • MIssed the link of the different projections... http://www.giss.nasa.gov/tools/gprojector/help/projections.html – e3495026 Dec 04 '16 at 03:33