Wrong geometry with mapbox queryRenderedFeatures - mapbox

I am trying to get the geometry of the polygon with queryRenderedFeatures
On zoom level 12 is ok, but on 15 I got the wrong geomentry.
Here is my code, and I everytime on mouse over I get the different coordinates.
Here https://codepen.io/benderlidze/pen/qPXNJv - hover the mouse from the top on the poly and from the bottom. The red poly is a geometry returned by queryRenderedFeatures and it is always different.
map.on("mousemove", "seatRowsFill", function(e) {
map.getCanvas().style.cursor = 'pointer';
map.setFilter("seatRowsFill-hover", ["==", "rowNumber", e.features[0].properties.rowNumber]);
var relatedFeatures = map.queryRenderedFeatures(e.point, { layers: ['seatRowsFill'],"filter": ["==", "rowNumber", e.features[0].properties.rowNumber] } )
console.log(relatedFeatures["0"].geometry.coordinates["0"][2])

At zoom 15, the geometry crosses a tile boundary. You can see this by adding map.showTileBoundaries = true: https://codepen.io/stevebennett/pen/XezJNB
From the documentation for queryRenderedFeatures():
Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple times in query results. For example, suppose there is a highway running through the bounding rectangle of a query. The results of the query will be those parts of the highway that lie within the map tiles covering the bounding rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple tiles due to tile buffering.
Instead of retrieving the geometry and then displaying that, it's usually better to have a separate layer which is just used for highlighting, then update the filter on that layer to match some property.
So, if you update the highlight layer's filter to be ['==', id, 500], then all the different pieces of that polygon will display correctly.
See the "Create a hover effect" example.

Related

Equivalent of map.cameraForBounds without a Mapbox GL Map instance

With Mapbox GL JS, I can get a center and zoom level to show all of a given bounding box using the cameraForBounds method:
const {center, zoom} = map.cameraForBounds(
[
[sw.lng, sw.lat],
[ne.lng, ne.lat],
],
{
padding: 20,
bearing,
pitch, // probably ignored?
},
);
(The typings say that pitch is OK but the docs don't mention it and neither does the implementation, so I assume it's ignored.)
This usually works great, but there occasionally situations where I want to figure out the center/zoom before the map is initialized. It seems like the bounds → camera transformation should just be math, not requiring a Map instance.
How can I go from bounds and bearing (and ideally pitch) to a center/zoom that will include those bounds without an instance of a Mapbox GL map?
You can't, for the simple reason that you don't know how many pixels this map will occupy. Setting a centre and zoom determines how much area (and which area) will be covered by each pixel in the middle of the map. The bigger the map DOM element is, the more area will be covered by the area within the map.

How to show building floors in Deck.gl/react-map-gl

So I'm currently working on Deck.gl and React and I need to load a geojson file which has an additional property named "floors" or something similar which tells how many floors the building has.
Is there any way to extrude alternating floors horizontally just a little bit so that it looks like a floor edge like some of the buildings in this image (although most of them just go thinner at the top). I tried searching the deck.gl but there is no such thing. I looked up and found MapBox-gl-js has something called an extrusion-base-height which lets you add polygon above another but there is no such thing as extruding horizontally to make 1 floor thinner and then back to the original size. This would give and edge whenever a new floor starts.
I have scoured the docs for deck.gl but couldn't find any thing on extruding horizontally or in another sense changing the polygon area/size so that I can draw multiple size polygons on the same spot.
Another clear picture of what I'm trying
Things I want to do.
The red polygon is tilted. Need to make it's orientation the same as the green one and reducing it's area at the same time.
Move the red polygon base at the top of the green polygon.
The test data I'm using is given below,
var offset = 0.00001;
var data = [{
polygon: [
[-77.014904,38.816248],
[-77.014842,38.816395],
[-77.015056,38.816449],
[-77.015117,38.816302],
[-77.014904,38.816248]
],
height: 30
},
{
polygon: [
[-77.014904 + offset ,38.816248],
[-77.014842 - offset, 38.816395 - offset],
[-77.015056 - offset, 38.816449 - offset],
[-77.015117 + offset, 38.816302],
[-77.014904 + offset, 38.816248]
],
height: 40
}
];
EDIT:- I think the proper way would be to convert longitude/latitude to Cartesian Coordinates, get the vectors to the 4 corners of the polygon translate the vectors to move towards the center by the offset amount then convert back. But this would only work with quad/rectangle polygon, for buildings that are made up of multiple quads I'd need another way.
If I'm understanding correctly, your problem boils down to: given a polygon (the footprint of the lower part of the building), generate a slightly smaller version of the same polygon, centered within it.
Fortunately, this is really easy using Turf's transformScale method.
So your steps will be:
Convert your polygon data into GeoJSON. (Which I assume you have some mechanism to do, in order to display it in Mapbox-GL-JS in the first place.)
Generate a smaller polygon using turf.transformScale(base, 0.9)
Add the new polygon with map.addSource
Display the new polygon with map.addLayer, setting the extrusion base height etc as required.

setting map boundary / edge when nowrap is engaged

Is there any way to adjust the bounds of the map so that the right-edge of Russia doesn't appear over to the left? You can see in the image I have a MultiPolygon area overlaying Russia but the map and the overlay are split. I'd like that tiny piece of the country to be on the right if possible!
Edge of Russia on the wrong side of the map:
A workaround I can think of is using the maxBounds property, where you would shift the default bounds slightly to the right, along with minZoom: 1. This won't prevent the user from seeing the world several times for a short time if zoomed out far / panning outside, as it says there:
... bouncing the user back when he tries to pan outside the view
var map = L.map('map',{
maxBounds:[ [-90, -160], [90, 200] ],
minZoom: 1
}).setView([66.058, 189.459], 4);
Demo
Welcome to SO!
If your multi polygon is the blueish area, then I am afraid you have to refactor your data in order to achieve what you want (shifting the left area onto the right, as if it were stitched back to Russia main land).
Your data (probably GeoJSON?) contains a separate polygon which longitudes are in the [-180, -120] range. Leaflet has no choice but to display it on the left of your map, independently from the noWrap option.
So you would need to dig into your data, and add 360 degrees longitude to every node of this polygon, so that they now sit in the range [180, 300].
Or somehow introduce a "detection" in your code that would perform the longitude addition automatically for shapes which bounds and/or center are far away (let's say in the [-180, -120] longitude range). Leaflet does not perform that operation automatically out-of-the-box.
Note: the noWrap option is for your Tile Layer not to load tiles outside that "central" world (in order to avoid showing multiple copies of the world). But in your case, you want to show a part of Russia / Siberia on an "adjacent copy of the world", so you might want to remove that option, or you will have your polygon not sitting over any basemap.

Prevent tiles outside of polygon from loading

Is there a way to prevent tiles from loading that are outside of a polygon? The closest example of this I found is here
http://jsfiddle.net/LsvDk/23/
var polygon = L.multiPolygon(
[
[
[
[51.509, -0.08],
[51.503, -0.08],
[51.503, -0.047],
[51.51, -0.047],
],
[
[84.774798, -176.835937],
[-85.221998, -175.078125],
[-85.103422, 176.484375],
[84.3916, 178.242188]
]
]
], {
color: '#DBDBDB',
weight: 1,
opacity: 1,
fillOpacity: 1
}).addTo(map);
It does what I want except it looks crappy when panning or zooming out because it loads tiles outside of the polygon.
If my understanding is correct, you do not like the example you mention because it uses a vector shape (polygon) to hide the tiles, but Leaflet has to re-render the shape when panning / zooming, which makes the background tiles temporarily appear?
EDIT:
Please have a try with TileLayer.BoundaryCanvas plugin. Instead of using a vector shape as mask, it combines tiles and its mask into a canvas. Hence the mask becomes a raster image, which avoids Leaflet behaviour of vector shape re-rendering at each pan / zoom, but instead keeps previous tiles (combined with mask in this case) until new ones are received.
Allows you to draw tile layers with arbitrary polygonal boundary. HTML5 Canvas is used for rendering.
The mask is specified as a GeoJSON shape, so it should be as easy to set up as in your example.
Remaining of original answer:
You would probably be interested in the bounds option of Tile Layer, to prevent any displaying of tiles outside a specified area.
When this option is set, the TileLayer only loads tiles that are in the given geographical bounds.
However, if your visible area does not perfectly fit with tiles boundaries, you would still have to use a mask to cover some areas, and you might still get the same issue with vector shapes being re-rendered.

mapbox - loading different geojson choropleth overlays at different zoom levels

so i have a mapbox base map and want to load a dynamic choropleth area map over the top of it. it will cover all continents. i've looked into doing this before using geojson but the resulting file was over 9mb. is there a way to have different geojson vectors loaded at different zoom levels as to reduce the file size ?
the reason the file was so big was because it was a very detailed vector overlay. basically i'd like to replicate the vectors of 'am map' but with the performance of mapbox, with much better vector resolution at bigger zoom levels. (http://www.amcharts.com/javascript-maps/)
the only other way to do this is by using geojson markers that are loaded on top of the base map, but this doesn't give the same visual impact of the fully coloured continent vectors..
any help would be greatly appreciated!
x
Well, you could watch zoomend on the map to add/remove overlays as needed.
map.on('zoomend', function() {
var currentZoom = map.getZoom();
overlayGroup.clearLayers() //remove existing
overlayGroup.addLayer(myZoomLevelMapping[currentZoom]) //add layer for this zoom level.
})