Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions demo/demo-labels.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<!DOCTYPE html>
<html>
<head>
<title>Leaflet Map Panes Example</title>
<meta charset="utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.2/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.0.2/dist/leaflet.js"></script>
<script src="http://localhost:4567/Leaflet.VectorGrid.bundled.js"></script>
</head>
<body style='margin:0'>
<div id="map" style="width: 100vw; height: 100vh"></div>

<script>

var map = L.map('map');

var url = 'https://{s}.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/{z}/{x}/{y}.vector.pbf?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpandmbXliNDBjZWd2M2x6bDk3c2ZtOTkifQ._QA7i5Mpkd_m30IGElHziw';

// Keep references of the markers created on a tile so that they can be
// removed when the tile is unloaded
function tileCoordsToKey(coords) {
return coords.x + ':' + coords.y + ':' + coords.z;
}
var markersCache = {}
function setMarkerToCache(tileCoords, marker) {
var tileKey = tileCoordsToKey(tileCoords);
markersCache[tileKey] = markersCache[tileKey] || [];
markersCache[tileKey].push(marker);
}
function clearTileMarkers(tileCoords) {
var tileKey = tileCoordsToKey(tileCoords);
var markers = markersCache[tileKey]
if (!markers) {
return;
}
for(var i = 0; i < markers.length; i++) {
map.removeLayer(markers[i]);
}
delete markersCache[tileKey];
}

var vectorTileOptions = {
rendererFactory: L.canvas.tile,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="https://www.mapbox.com/about/maps/">MapBox</a>',
pointFeatureHooks: {
place_label: function(properties, tileCoords, point) {
if(properties.localrank > 60) {
marker = new L.Marker(map.unproject(point));
marker.bindTooltip(properties.name).openTooltip();
map.addLayer(marker);
setMarkerToCache(tileCoords, marker);
}
}
},
vectorTileLayerStyles: {

water: {
weight: 0,
fillColor: '#9bc2c4',
fillOpacity: 1,
fill: true,
stroke: false
},

admin: [],
state_label: [],
country_label: [],
marine_label: [],
state_label: [],
place_label: function(properties) {
if(properties.localrank > 60) {
return {};
} else {
return [];
}
},
waterway_label: [],
landuse: [],
landuse_overlay: [],
road: [],
poi_label: [],
waterway: [],
aeroway: [],
tunnel: [],
bridge: [],
barrier_line: [],
building: [],
road_label: [],
housenum_label: [],

}
};

var pbfLayer = L.vectorGrid.protobuf(url, vectorTileOptions).addTo(map);
pbfLayer.on('tileunload', function(e) {
clearTileMarkers(e.coords);
})

map.setView({ lat: 47.040182144806664, lng: 9.667968750000002 }, 6);


</script>
</body>
</html>
18 changes: 18 additions & 0 deletions src/Leaflet.VectorGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ L.VectorGrid = L.GridLayer.extend({
options: {
rendererFactory: L.svg.tile,
vectorTileLayerStyles: {},
pointFeatureHooks: [],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer this to be oneachfeature, to align better with L.GeoJSON - it's essentially the same functionality.

Also, I'd prefer to keep it simple and allow one hook - again, keep things similar to existing API to avoid surprises.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few reasons why I did it this way:

  • the concept of feature in a vt seems less clear than in geojson to me
  • vt have layers not geojson, it separates things neatly
  • I see it more like vectorTileLayerStyles functions, it is external styling by the user
  • It may not imitate L.GeoJSON but it imitates VectorGrid existing options
  • a single callback would be executed for very dense vt layers that the user may not be interested in. This includes the user's code and the pre-processing on our side (like transforming tile coords into map coords)

interactive: false
},

Expand Down Expand Up @@ -43,6 +44,8 @@ L.VectorGrid = L.GridLayer.extend({
var layerStyle = this.options.vectorTileLayerStyles[ layerName ] ||
L.Path.prototype.options;

const pointFeatureHook = this.options.pointFeatureHooks[ layerName ]

for (var i in layer.features) {
var feat = layer.features[i];
var id;
Expand All @@ -60,6 +63,21 @@ L.VectorGrid = L.GridLayer.extend({
}
}

// The user asked to be notified if point type features added to the current vt layer
if (pointFeatureHook && feat.type === 1) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you see any particular reason this should only be done for points?

IMHO, adding this functionality for any feature would be more powerful and avoid cluttering the API with type specific hooks.

If added as oneachfeature, we could pass it the created layer as well as the original feature, more or less exactly as L.GeoJSON does. Also, I imagine doing it this way would heavily reduce the amount of code needed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Features other than points can be split into multiple tiles no ? So the callback will be called multiple times for the same actual object on the map right ? It seemed to me that it would not be easy to manage for the user. Points are straightforward. Butl I agree, I would love to make it more generic.

It did not feel right to pass the created layer or the vt feature as they are not standard objects and their geometries are relative to the tile they are in. I did not want to let the user make the calculations to transform coords in the tiles into coords in the map. Also if the user chooses to configure empty style on a vt layer and create a marker manually instead of letting VectorGrid render a pseudo marker, then there is no layer to be created, rendered and sent back to the user by VectorGrid.

var pointCoord = feat.geometry[0];
var offset = coords.scaleBy(tileSize);
var point;
if (typeof pointCoord[0] === 'object' && 'x' in pointCoord[0]) {
// Protobuf vector tiles return [{x: , y:}]
point = L.point(offset.x + (pointCoord[0].x * pxPerExtent), offset.y + (pointCoord[0].y * pxPerExtent));
} else {
// Geojson-vt returns [,]
point = L.point(offset.x + (pointCoord[0] * pxPerExtent), offset.y + (pointCoord[1] * pxPerExtent));
}
var userDefinedLayer = pointFeatureHook(feat.properties, coords, point)
}

if (styleOptions instanceof Function) {
styleOptions = styleOptions(feat.properties, coords.z);
}
Expand Down