mapbox / mapbox-gl-js

Interactive, thoroughly customizable maps in the browser, powered by vector tiles and WebGL
https://docs.mapbox.com/mapbox-gl-js/
Other
11.21k stars 2.22k forks source link

Show long/lat grid lines #10093

Open henriktorget opened 4 years ago

henriktorget commented 4 years ago

Question

I am trying to add labeled latitudinal/longitudinal to my map like shown in image below. image

I can't seem to find a good way to do this from reading the documentation.

There is some implementations for this in leaflet, but the bindings for leaflet are experimental only and not suited for production. https://github.com/mapbox/mapbox-gl-leaflet

Am I missing something? Or is this possible to make in mapbox studio or programatically with mapbox-gl?

Thanks

Edit: Graticules is what I refer to by grid lines.

mourner commented 4 years ago

@henriktorget you can do this programmatically with a GeoJSON source, generating a bunch of lines and then using this as a source with line and label layers. Something like this:

const graticule = {
    type: 'FeatureCollection',
    features: []
};
for (let lng = -170; lng <= 180; lng += 10) {
    graticule.features.push({
        type: 'Feature',
        geometry: {type: 'LineString', coordinates: [[lng, -90], [lng, 90]]},
        properties: {value: lng}
    });
}
for (let lat = -80; lat <= 80; lat += 10) {
    graticule.features.push({
        type: 'Feature',
        geometry: {type: 'LineString', coordinates: [[-180, lat], [180, lat]]},
        properties: {value: lat}
    });
}

map.on('load', () => {
    map.addSource('graticule', {
        type: 'geojson',
        data: graticule
    });
    map.addLayer({
        id: 'graticule',
        type: 'line',
        source: 'graticule'
    });
    ...

It might be a good idea to create a plugin for this to make it easier though.

henriktorget commented 3 years ago

@mourner Thanks a lot for this!

I have been trying to do something similar. But I recently found a dataset for different "resolution" graticule grids that i could upload to mapbox studio and toggle on zoom level. Which works well enough for now.

If I feel that the datasets are limiting, I will adopt your idea.

prafeb commented 3 years ago

@henriktorget I am also trying to solve this. Could you please let me know what dataset you are using. I would also like to try that solution of uploading that dataset to mapbox.

Thanks for sharing this.

letelliercle commented 3 years ago

@henriktorget I'm also interested by the dataset solution you mentioned but I did not find any graticule grids on the web. Could you please provide me a link ?

Thanks !

henriktorget commented 3 years ago

Hey @letelliercle @prafeb!

What we are doing now is creating tiles from premade graticules. So i found these files with different resolutions:

https://www.naturalearthdata.com/downloads/10m-physical-vectors/10m-graticules/

These files can be dragged and dropped into mapbox studio as sources, and toggled on/of programatically. You can also set the max min zoom for the layers, so you get the prefered zoom level for each grid.

image

But going forward, i think we will try to do what @mourner is demonstrating. At least if you want to interact with the boxes, you might want to create them on the fly.

Edit:

So if i want to make boxed i can interact with, i would modify @mourner's code to create the grid that is within the viewport. (you have the top left and bottom right coordinates from events when the map is moved), and then create lines or boxes incrementally between these. Then you can add mouse over events to the boxes that highlights them.

PaparazzoKid commented 1 year ago
const graticule = {
    type: 'FeatureCollection',
    features: []
};
for (let lng = -170; lng <= 180; lng += 10) {
    graticule.features.push({
        type: 'Feature',
        geometry: {type: 'LineString', coordinates: [[lng, -90], [lng, 90]]},
        properties: {value: lng}
    });
}
for (let lat = -80; lat <= 80; lat += 10) {
    graticule.features.push({
        type: 'Feature',
        geometry: {type: 'LineString', coordinates: [[-180, lat], [180, lat]]},
        properties: {value: lat}
    });
}

map.on('load', () => {
    map.addSource('graticule', {
        type: 'geojson',
        data: graticule
    });
    map.addLayer({
        id: 'graticule',
        type: 'line',
        source: 'graticule'
    });
    ...

Apologies for re-opening but how could this be modidied to show minutes as well as degrees?

henriktorget commented 1 year ago

I guess you could try something like this, where sixty minutes is one degree:



one_minute = 1 / 60

for (let lng = -170; lng <= 180; lng += one_minute) {
    graticule.features.push({
        type: 'Feature',
        geometry: {type: 'LineString', coordinates: [[lng, -90], [lng, 90]]},
        properties: {value: lng}
    });
}
for (let lat = -80; lat <= 80; lat +=one_minute) {
    graticule.features.push({
        type: 'Feature',
        geometry: {type: 'LineString', coordinates: [[-180, lat], [180, lat]]},
        properties: {value: lat}
    });
}`
PaparazzoKid commented 1 year ago

Splendid, thanks!!

kevando commented 11 months ago

This is so awesome. Thanks for sharing this code. I want to use it with this example, but each time I toggle to a new style, the graticules are no longer visible. Any ideas?

https://docs.mapbox.com/mapbox-gl-js/example/setstyle/

paulsUsername commented 8 months ago

TLDR; you need to re add everything when you set a new style.

@kevando When you set a style you are setting a new style sheet (style.json) so the items you added to the existing stylesheet are gone (as they are set to the previous stylesheet).