jreid42
jreid42

Reputation: 1250

Matching leaflet zoom level with d3 geo scale

What do I want to do?

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

Upvotes: 3

Views: 2237

Answers (2)

Alexander Kachkaev
Alexander Kachkaev

Reputation: 908

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

Upvotes: 1

jreid42
jreid42

Reputation: 1250

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.

Upvotes: 4

Related Questions