If current bounds lie in others (many) bounds? - javascript

I am using google maps v3.
I want to know IF current map bounds lies in a sum of all previous bounds (all points of current bounds are covered by other bounds) (if not, I will load markers for current bounds via ajax)
Let say, I have an array of previous viewport bounds, that I do so:
google.maps.event.addListener(map, 'idle',function() {
var bounds = map.getBounds();
boundsArray.push(bounds);
});
The algorithm to check that I invented by now is a hard one and it is the following:
1) Generate all edge points (lan/lon) of current bounds with some $step (0.0001?) and put them in "pointsArray".
2) Iterate over pointsArray, and check if current point exists in at least one previous bounds:
From LatLngBounds doc: contains(latLng:LatLng) boolean Returns true if the given
lat/lng is in this bounds.
so, something like:
if (boundsArray[y].contains(pointsArray[i]) {
and if contains, then remove that point from pointsArray
3) Finally, after all iterations, if the pointsArray is empty — it means all points of current bounds are inside another/s bounds and function returns true...
There are in my opinion 2 disadvantages of my algorithm:
1) it is not 100% precise (depends on $step)
2) and that is worse, it my cause performance drop on a client, checking so many points by all bounds... as javascipt is implemented on users PC.
So, if the any more precise & easy, faster solution for my problem? maybe using another math approaches, google geometry library & so on? How to do that correctly?

You can delete the bounds array when it becomes too big. Then you can start again. It's much easier then to compare the bounds.

Related

Slow updating HereMaps DomIcon when location (lat / long) changes, re-render

Using renderToStaticMarkup to render html to a DomIcon in HereMaps. That's working and quick when there's not that many markers and updates. However, 100 markers frequently updating and re-rendering is causing a slow page open. It's even slowing the map render.
I've looked into best practices around re-using a DomIcon. I've also been looking into clustering -- but not sure how updating would work. Is clustering the only way to go further here? Curious if there's any other best practices for performance
Clustering solves the potential performance issues, however H.map.Group is used to associate the markers together and the group.getBounds() method to find the minimal bounding box holding all of the group's contents. The map.viewBounds() can then be updated.
function addMarkersAndSetViewBounds() {
// create map objects
var toronto = new H.map.Marker({lat:43.7, lng:-79.4}),
boston = new H.map.Marker({lat:42.35805, lng:-71.0636}),
washington = new H.map.Marker({lat:38.8951, lng:-77.0366}),
group = new H.map.Group();
// add markers to the group
group.addObjects([toronto, boston, washington]);
map.addObject(group);
// get geo bounding box for the group and set it to the map
map.getViewModel().setLookAtData({
bounds: group.getBoundingBox()
});
}
forEachDataPoint (callback)
This method invokes the specified callback for each data point in the given cluster which can work for updating the data points.
A clustering algorithm groups data points by collapsing two or more
points positioned close to one another on the screen into a single
cluster point. All other (not collapsed) points are still visible on
map as noise points.
If this actually suits the use case, go for clustering. It helps to uplift the performance issue. for more details refer :
developer.here.com/documentation/maps/topics/clustering.html

Detect when user reaches maxBounds using Leaflet

I am using leaflet to show an interactive map to our users.
We want to let them browse through a limited area, and inform them they have to subscribe in case they want to see something too far away (using a pop up or equivalent).
So far I have seen that Leaflet supports a maxBounds option.
This is a good start that lets me prevent users to see larger areas.
Now I would like to be able to detect a maxBounds 'event' to show the user a pop up.
I have been looking into the Leaflet source code, but couldn't find an obvious way to do it.
so far I have found that the maxBounds option is fed into the setView method.
This method itself uses the _limitCenter method to define the center.
This goes a few levels deeper, down to the _getBoundsOffset method that finally uses the bounds.
_getBoundsOffset: function (pxBounds, maxBounds, zoom) {
var projectedMaxBounds = toBounds(
this.project(maxBounds.getNorthEast(), zoom),
this.project(maxBounds.getSouthWest(), zoom)
),
minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
dx = this._rebound(minOffset.x, -maxOffset.x),
dy = this._rebound(minOffset.y, -maxOffset.y);
return new Point(dx, dy);
},
The closest I could find so far would be to hook into the moveend event and check whether the center is out of my bounds manually.
However, it seems like this would be redundant with what leaflet is already doing.
Is there a better to leverage leaflet to achieve this?
Thanks
Just check if your defined bounds contain the map bounds. As long as the map bounds are inside the defined bounds, this will do nothing:
var myBounds = L.latLngBounds(...)
map.on('move moveend zoomend', function(){
if (!myBounds.contains(map.getBounds())) {
// Display popup or whatever
}
});
it seems like this would be redundant with what leaflet is already doing.
Don't worry about that. The overhead is negligible for this use case.

How do you get the currently visible features in ol3 when the map was been rotated?

To get the current features I use
var extent = map.getView().calculateExtent(map.getSize());
var features = layer.getSource().getFeaturesInExtent(extent);
However when a user spins the map around (e.g scrolling left 20 times.) this no longer works.
I can't figure out the function I need to use to map the current extent into the 'base' extent of the projection.

Exclude overlaid element from Google Maps viewport bounds

I am using Google Maps API v3 to create an inline map on a website. In its container element, I also have an absolute positioned overlay which shows some detail information, visually hovering over the map. Determining on context this element may grow up to the size of the entire map element.
All this is working fine, however the Maps instance of course still considers the overlaid part of the map a valid usable part of the map. This means that, especially if the overlay is at maximum height, setCenter doesn't focus on the visible center, and routes drawn with DirectionsRenderer are partially underneath the overlay.
See this image:
Is there a way to limit the actual viewport to the blueish area, so that setCenter centers on the arrow tip and setBounds fits to the blue part?
I have managed to implement an acceptably functional workaround for the time being.
Some general notes which are good to know:
Every Map object has a Projection, which can convert between LatLng points to map points.
The map points a Projection uses for calculation are in 'world' coordinates, meaning they are pixels on the world map at zoom level 0.
Every zoom level exactly doubles the number of pixels shown. This means that the number of pixels in a given map point equals 2 ^ zoom.
The samples below assume a 300px wide sidebar on the right - adapting to other borders should be easy.
Centering
Using this knowledge, it becomes trivial to write a custom function for off-center centering:
function setCenter(latlng)
{
var z = Math.pow(2, map.getZoom());
var pnt = map.getProjection().fromLatLngToPoint(latlng);
map.setCenter(map.getProjection().fromPointToLatLng(
new google.maps.Point(pnt.x + 150/z, pnt.y)));
}
The crucial bits here are the z variable, and the pnt.x + 150/z calculation in the final line. Because of the above assumptions, this moves the point to center on 150 pixels to the left for the current zoom level, and as such compensates for the missing 300 pixels on the right sidebar.
Bounding
The bounds issue is far less trivial. The reason for this is that to offset the points correctly, you need to know the zoom level. For recentering this doesn't change, but for fitting to previously unknown bounds it nearly always will. Since Google Maps uses unknown margins itself internally when fitting to bounds, there is no reliable way to predict the required zoom level.
Thus a possible solution is to invoke a two-step rocket. First off, call fitBounds with the entire map. This should make the bounds and zoom level at least nearly correct. Then right after that, do a second call to fitBounds corrected for the sidebar.
The following sample implementation should be called with a LatLngBounds object as parameter, or no parameters to default to the current bounds.
function setBounds(bnd, cb)
{
var prj = map.getProjection();
if(!bnd) bnd = map.getBounds();
var ne = prj.fromLatLngToPoint(bnd.getNorthEast()),
sw = prj.fromLatLngToPoint(bnd.getSouthWest());
if(cb) ne.x += (300 / Math.pow(2, map.getZoom()));
else google.maps.event.addListenerOnce(map,'bounds_changed',
function(){setBounds(bnd,1)});
map.fitBounds(new google.maps.LatLngBounds(
prj.fromPointToLatLng(sw), prj.fromPointToLatLng(ne)));
}
What we do here at first is get the actual points of the bounds, and since cb isn't set we install a once-only event on bounds_changed, which is then fired after the fitBounds is completed. This means that the function is automatically called a second time, after the zoom has been corrected. The second invocation, with cb=1, then offsets the box to correct for the 300 pixel wide sidebar.
In certain cases, this can lead to a slight off-animation, but in practice I've only seen this occur when really spamclicking on buttons causing a fit operation. It's running perfectly well otherwise.
Hope this helps someone :)
You can use the map panBy() method which allows you to change the center of the map by a given distance in pixels.
Hope this helps!
I had a similar need and ended up just forcing some "padding" to the east of a LatLngBounds object.
On the upside, it's simple and it works. On the downside it's not really versatile. Just a quick little hack.
// start with a standard LatLngBounds object, extending as you usually would...
bounds = new google.maps.LatLngBounds();
// ...
ne = bounds.getNorthEast();
sw = bounds.getSouthWest();
// the multiplier used to add space; positive for east, negative for west
lngPadding = 1.5
extendedLng = ne.lng() + (ne.lng() - sw.lng()) * lngPadding;
// copy original and extend with the new Lng
extendedBounds = bounds;
extendedBounds.extend(new google.maps.LatLng(ne.lat(), extendedLng));
map.fitBounds(extendedBounds);

How do i determine the Max Zoom Level integer for Google Maps?

Is there a way of generating the actual integer that represents the max zoom level of Google Maps? In either the Static or Java API?
Yes you can generate the maximum zoom level possible for the place you are looking at as:
getMaxZoomAtLatLng(latlng:LatLng, callback:function(MaxZoomResult))
Returns the maximum zoom level available at a particular LatLng for the Satellite map type. As this request is asynchronous, you must pass a callback function which will be executed upon completion of the request, being passed a MaxZoomResult.
You can also set the maximum allowed zoom level (to prevent users from fully zooming in for instance) by using the maxZoom property of your MapOptions
The maximum zoom level which will be displayed on the map. If omitted, or set to null, the maximum zoom from the current map type is used instead.
Read everything about it here. (CTRL+F and look for "maximum zoom")
http://code.google.com/apis/maps/documentation/javascript/v2/introduction.html
Each map also contains a zoom level, which defines the resolution of the current view. Zoom levels between 0 (the lowest zoom level, in which the entire world can be seen on one map) to 19 (the highest zoom level, down to individual buildings) are possible within the normal maps view. Zoom levels vary depending on where in the world you're looking, as data in some parts of the globe is more defined than in others. Zoom levels up to 20 are possible within satellite view.
Seems like it's relatively safe to just hard code 19, but if you need the exact max for the places where 19 zoom is disallowed (military bases and whatnot) or places where 20 is allowed (not sure), I'm not sure how to determine that. Perhaps you can detect this by setZoom and then immediately calling getZoom and if the number returned from getZoom is not the one you just set, then you're in one of the non-standard locations.
Here's actual code, if it's helpful.
The accepted answer points in the right direction. The documentation you want is [right here][1].
And here's working modern ES6 code for 2019:
/* Determine max zoom at location */
const location = { lat: _LATITUDE_, lng: _LONGITUDE_ }
const getMaxZoom = new google.maps.MaxZoomService()
getMaxZoom.getMaxZoomAtLatLng(location, (response) => {
console.log('Max zoom at this location:', response.zoom)
})
[1]: https://developers.google.com/maps/documentation/javascript/maxzoom

Categories

Resources