3

What do I want to do?

  • Overlay an SVG layer on top of leaflet (or any map framework).
  • Using d3js and d3.geo.mercator position elements in my SVG layer so they are positioned correctly over leaflet.
  • I only have access to the zoom level, the center of the map (lat/long), the width and height of the map SVG element.
  • I should only have to listen to changes to zoom level and map center in order to know when to reposition my SVG elements.

I've already made the SVG element the same size as the leaflet layer; and have it as an overlay.

How am I doing it now?

I have some globals that line up with leaflet's values (Map.zoom, Map.width, Map.height, Map.center).

Map.center = [43, -72];
Map.zoom = 3;

Map.projection = d3.geo.mercator()
            .center([Map.center[1], Map.center[0]])
             // I think this is the problem. I don't really understand this.
            .scale(256 * Math.pow(2, Map.zoom))
            .translate([Map.width / 2, Map.height / 2])

Map.lt = new L.Map...(center is Map.center, zoom is Map.zoom)

Any time leaflet changes I update the global values and recalculate Map.projection.

I have a function:

function transform(d) {
    d = Map.projection(d.value.geometry.coordinates[0], d.value.geometry.coordinates[1]);

    return "translate(" + d[0] + "," + d[1] + ")";
}

Then on objects and elements I need to position I call:

object.attr("transform", transform);

My problem is that the layers are not in sync.

When I center my map on [43, -72] and I look at the x,y value returned (from Map.projection) it is exactly in the middle of my SVG layer, so I am guessing there is something wrong with my scale. I am not sure how I can make these values line up correctly.

I don't want to use d3.geo.tile, or d3.geo.path() as I need more flexibility in control over the positioning and animation of my elements.

I just want to know how I can line up leaflets positioning with my SVG layer, only by utilizing d3 functions. I dont wish to rely on leaflet for positioning my items as I might not use leaflet in the future; I just need to be able to scale/translate my d3.geo.mercator projection so it lines up with leaflet. How can I do this?

Thanks!

Example

2
  • Have you seen this tutorial? What kind of additional customisation do you need that d3.geo.path doesn't allow you to do? Commented Feb 18, 2014 at 16:43
  • Yes I have. Using d3 in this way doesn't really fit into the way I want to structure the application. This also uses leaflet for the projection - which is something I don't want to do since I am probably going to dump leaflet later in time. I simply want to know how to perform the same calculation leaflet does, so that I can arbitrarily line up my elements with any map framework as long as I know its center, its zoom level, and the width/height of my map.
    – jreid42
    Commented Feb 18, 2014 at 16:54

2 Answers 2

4
Map.projection = d3.geo.mercator()
            .center([Map.center[1], Map.center[0]])
            .scale((1 << 8 + Map.zoom) / 2 / Math.PI)
            .translate([Map.width / 2, Map.height / 2])

Not sure how that works, but looked at some other examples and figured it out.

0
1

Here's what I ended up with (don't be surprised about the strangeness of JS – it's ES6):

const convertZoomLevelToMercator = (zoomLevel) =>
  Math.pow(2, 8 + zoomLevel) / 2 / Math.PI;
const convertZoomLevelFromMercator = (zoomLevelInMercator) =>
  Math.log(zoomLevelInMercator * 2 * Math.PI) / Math.LN2 - 8;

Testing:

console.log(convertZoomLevelToMercator(1));
console.log(convertZoomLevelToMercator(5));
console.log(convertZoomLevelToMercator(10));
console.log(convertZoomLevelToMercator(15));
console.log(convertZoomLevelToMercator(20));
console.log('----');
console.log(convertZoomLevelFromMercator(convertZoomLevelToMercator(1)));
console.log(convertZoomLevelFromMercator(convertZoomLevelToMercator(5)));
console.log(convertZoomLevelFromMercator(convertZoomLevelToMercator(10)));
console.log(convertZoomLevelFromMercator(convertZoomLevelToMercator(15)));
console.log(convertZoomLevelFromMercator(convertZoomLevelToMercator(20)));

Output:

81.48733086305042
1303.797293808806
41721.51340188181
1335088.428860218
42722829.72352698
----
1
5
10
15
20

Not the answer you're looking for? Browse other questions tagged or ask your own question.