Open drclanc-oss opened 1 month ago
Perhaps I was wrong about zIndex
being encoded directly in the vector tiles.
import httpx
import mapbox_vector_tile
tile = httpx.get("https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/tile/8/128/125.pbf", verify=False)
decoded_data = mapbox_vector_tile.decode(tile.content)
decoded_data["Bathymetry"]
That gives:
{'extent': 1048576,
'version': 2,
'features': [{'geometry': {'type': 'Polygon',
'coordinates': [[[1064960, 1064960],
[1064960, -16384],
[-16384, -16384],
[-16384, 1064960],
[1064960, 1064960]]]},
'properties': {'_symbol': 0},
'id': 0,
'type': 'Feature'},
{'geometry': {'type': 'Polygon',
'coordinates': [[[1064960, 1064960],
[1064960, -16384],
[-16384, -16384],
[-16384, 1064960],
[1064960, 1064960]]]},
'properties': {'_symbol': 1},
'id': 0,
'type': 'Feature'},
{'geometry': {'type': 'Polygon',
'coordinates': [[[1064960, 1064960],
[1064960, -16384],
[-16384, -16384],
[-16384, 1064960],
[1064960, 1064960]]]},
'properties': {'_symbol': 2},
'id': 0,
'type': 'Feature'},
{'geometry': {'type': 'Polygon',
'coordinates': [[[-16384, -16384],
[-16384, 1064960],
[1064960, 1064960],
[1064960, -16384],
[-16384, -16384]]]},
'properties': {'_symbol': 3},
'id': 0,
'type': 'Feature'},
{'geometry': {'type': 'Polygon',
'coordinates': [[[1064960, 1064960],
[1064960, 244623],
[1038487, 245318],
[1031516, 236286],
[1023680, 230237],
[1022483, 173782],
[1031516, 166810],
[1037506, 159048],
...
...
...
[613505, 65588],
[621037, 58609],
[626230, 53006],
[631833, 47813],
[638823, 40268],
[673563, 39614]]]},
'properties': {'_symbol': 4},
'id': 0,
'type': 'Feature'}],
'type': 'FeatureCollection'}
There doesn't appear to be a zIndex
or anything else to indicate layer draw order. How do we control which layers draw on top and which on bottom?
Hi @drclanc-oss . Unfortunately, I don't think there is a way to control the order.
However, you can try adding multiple leaflet layers with different vectorTileLayerOptions
:
from ipyleaflet import Map, VectorTileLayer
world_tiles_url = "https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/tile/{z}/{y}/{x}.pbf"
bathymetry_style='''{
"Graticule/label":[],
"Graticule":[],
"Marine area":[],
"Bathymetry": function(properties, zoom, geometryDimension){
L.Path.mergeOptions({stroke:false}); // HACK
var color;
# ... your code to assign color based on the property
return {
fillColor: color,
fill: true,
weight: 0,
opacity: 1,
fillOpacity: 1
}
}
}
'''
marine_area_style='''{
"Marine area": {"fillOpacity": 1, "color": "#e7f9ff", "fill": true},
}
'''
bathymetry = VectorTileLayer(
url=world_tiles_url,
vector_tile_layer_styles=bathymetry_style,
name="Bathymetry Basemap"
)
marine_areas = VectorTileLayer(
url=world_tiles_url,
vector_tile_layer_styles=marine_area_style,
name="Marine areas"
)
m = Map(center=[30, -65], zoom=4)
[m.remove(lyr) for lyr in m.layers]
m.add(marine_areas)
m.add(bathymetry)
bathymetry.redraw() # Hack.
m
I think this is what you are looking for, right?
However, there is one problem with your vector tile layer: it seems there are extra Line features that are not contained in any "layer", i.e. they are not coming from either of Graticule/label
, Graticule
, Marine area
, or Bathymetry
, so even if I set all of these to an empty style ([]
), you can still see those features being drawn with the default L.path
options:
In leaflet.js, you could update L.path
directly (see this jsfiddle example) -- but in ipyleaflet I don't think there is a way to do this.
In the code I shared above, I did it in a hacky way.. I updated L.path
inside the function for the Bathymetry
layer. However, because some of these extra features are drawn before this update, we have to redraw the layer. I tested this on vscode and the update to L.path
seems to persist within the same notebook (until you close it/reopen it).. so keep that in mind if you rely on the default style.
Thanks @lopezvoliver I appreciate your insight.
I am trying to make an ocean map layer, including bathymetry, from this Esri vector tile service. I am able to style the various bathymetry depths by providing a JavaScript string to the
vector_tile_layer_styles
param on theVectorTileLayer
constructor and it works well.'0.19.2'
This looks pretty good!
However, the water areas nearest the coastlines is not filled. This is because the required features are in the "Marine area" layer; when I uncomment the "Marine area" style in my
jstyle
above those areas are indeed filled, but the features cover up my bathymetry.When I look at these vector tiles using Esri's style editor the layers draw in the correct order: https://www.arcgis.com/apps/vtseditor/en/#/7dc6cea0b1764a1f9af2e679f642f0f5/layers
Can I control the layer draw order somehow? From everything I've read, this is controlled in the vector tiles themselves via the
zIndex
. In this case I want theMarine area
layer to draw underneath theBathymetry
layer instead of on top of it.