I'm building a store finder and would like to implement the following workflow:
User enters a location
We geocode it using Google Geocoder
We examine the geocoded results for the bounding box
We zoom the map in to fit the specified bounds and query for stores within those bounds
If there are stores within the map bounds, display them all on the map
But if there now no stores within the map bounding box, zoom one level further out, and repeat (5) until we find some stores
The above is fine, but it would be better UX if the map did not actually appear to move until some stores have been found.
So, is it possible to query Google Maps as follows: Given a bounding box, can we find the the bounds of the correctly zoomed Google Map that contains that particular bounding box, without actually loading the Google Map?
I'm not sure it's possible, because it depends on the width of the map div in my page, I guess.
geocoder.geocode( {'address': search_text }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.panTo(results[0].geometry.location);
map.fitBounds(results[0].geometry.bounds);
var bounds_to_check_for_stores = map.getBounds();
// QUESTION: get bounds_to_check_for_stores without the three preceding steps?
}
}
The google.maps.LatLngBoundsapi-doc has a method: extend(point:LatLng) which will allow you to extend a bounds to ensure that the bounds are adjusted to include the LatLng point passed as a parameter. The extend method returns a LatLngBounds that has been adjusted.
With this, you could:
Expand the radius of your geocode lookup until you find appropriate stores.
Find the closest store or group of stores that you wish to use.
Retrieve the current bounds from the map: Map.getBounds().
Call LatLngBounds.extend to adjust the bounds, using the .geometry.location property of the stores you selected in Step 2.
Call Map.panToBounds to change the map's viewport, keeping the user's experience simple, because only one change to the view will be performed.
Related
I have a LeafletJS map with a GeoJSON layer that contains multiple polygons. If a user enters a latitude/longitude coordinate that falls within the GeoJSON layer, the script should retrieve the feature that contains that point and log some information about it to the console.
I can't simply use Leaflet's built-in event handling because the latitude and longitude coordinates are generated by a separate input field, not by direct interaction with the map. So my question is not a duplicate of this.
I'm looking for something similar to getFeatureContainingLatLng() in the example below:
var map = L.map('map');
var geojson = L.geoJson(myGeojson);
geojson.addTo(map);
$.on('address input changed event', function(lat, lng) {
var myFeature = geojson.getFeatureContainingLatLng(lat, lng);
console.log(myFeature.properties);
});
The plugins Leaflet.CheapLayerAt or Leaflet-pip should help. Both approaches will solve your problem, albeit they have different advantages and disadvantages specially in terms of algorithmic complexity.
I am trying to figure out how to capture the four corners of a Leaflet window, so that I might load points based on where a user is zoomed in geographically. For example, have a look here.
Now, how would I capture the four corners of that view so that I could load only the points that are geometrically bounded to that general location - downtown London?
I have not been able to find any example for Leaflet specifically. Any assistance will be appreciated.
The term you should be searching with is called "bounds". Leaflet's L.Map has a method called getBounds which will return the bounds of the current mapview:
http://leafletjs.com/reference.html#map-getbounds
Returns the LatLngBounds of the current map view.
It returns a LatLngBounds object which consist of a southwest and a northeast LatLng object:
http://leafletjs.com/reference.html#latlngbounds
Represents a rectangular geographical area on a map.
How you use those bounds to query your points of interest from your server depends on the platform you are working with.
If you already have a dataset loaded and you want filter that based on the current bounds you could use contains method of the LatLngBounds object. You can use that to check if a point is contained within the current bounds:
http://leafletjs.com/reference.html#latlngbounds-contains
Returns true if the rectangle contains the given point.
This is somewhat similar to the question asked here --
I'm writing a search box for a map application, which retrieves a whole set of search results (people's names & info) at once from a server and then pages through the list of results. So at any given point on the map there are two kinds of markers -- a background marker for points which are in the search results but not in the current page, and a foreground marker for points which are in the current page of search results.
All this works nicely.. what I'd like to do now is set it up so that if a user zooms or pans the map, the search results list updates to show only markers within the current map bounds.
Obviously there are server-side ways to do this, or I could also just run through the whole list of markers to see which fit within the current bounds; but does anybody know a built-in way to do this within leaflet? Something which would look like map.getVisibleLayers()?
I think this may be of help:
https://github.com/stefanocudini/leaflet-list-markers
as you can see from the demo, including all markers in a layer, this plugin shows a list of only those visible in the current viewport.
Its usage is simple, in a row:
var markersLayer = new L.LayerGroup();
map.addControl( new L.Control.ListMarkers({layer: markersLayer}) );
The code for obtain it is like as:
var layers = L.LayerGroup(), //layers contains all markers..
contained = []; //makers in map boundingbox
layers.eachLayer(function(l) {
if( l instanceof L.Marker && map.getBounds().contains(l.getLatLng()) )
contained.push(l);
});
Here's a fully working function that does the job:
// var map is an instance of a Leaflet map
// this function assumes you have added markers as GeoJSON to the map
// it will return an array of all features currently shown in the
// active bounding region.
function getFeaturesInView() {
var features = [];
map.eachLayer( function(layer) {
if(layer instanceof L.Marker) {
if(map.getBounds().contains(layer.getLatLng())) {
features.push(layer.feature);
}
}
});
return features;
}
You have to check the bounds of each layer versus the map's bounds. Because eachLayer() returns all layers regardless of whether they are in the visible extent.
if(map.getBounds().contains(layer.getLatLng())) { ... }
In Stefano's code, this is shown around this line:
https://github.com/stefanocudini/leaflet-list-markers/blob/master/src/leaflet-list-markers.js#L95
Regarding the last part of your question, if you want to iterate through visible layers, you can use eachLayer, e.g:
map.eachLayer(function (layer) {
// do something with the layer
});
API reference: http://leafletjs.com/reference.html#map-stuff-methods
I have used the point based clustering http://bingmapsv7modules.codeplex.com/wikipage?title=Point%20Based%20Clustering
This is great for clustering. However, I am trying to find the longitude latitude of a cluster which contains first pushpin every 5 second using setInterval...obviously I am just simplifying the scenario..
var pushpin = pinLayer[0]; grabbing first story of the pin
var currentClusterIndex = pushpin._clusterIndex; // get the cluster index
var currentCluster = pinLayer.GetPinByClusterIndex(currentClusterIndex);// get cluster info
var currentLatitude = currentClusterLocation.latitude; // get latitude
var currentLongitude = currentClusterLocation.longitude; // get longitude
Now the latitude and longitude is correct when the map loads, but right after user interaction it's rarely correct, it's giving latitude longitude of different cluster.
Could someone possibly help with this?
It's not 100% clear what you want to do. Your code seems to indicate you want to take a pushpin that's on the map and get it's cluster index and use that to get a reference the pushpin. This will just give you a reference the pushpin you are ready have. I suspect you want to get the data for the pushpin which you can easily do by passing the cluster index into the GetDataByClusterIndex method. This will return an array of all the raw data that is in cluster. You can then grab the item you want and get it's original coordinate.
I have a list of lat long pairs. (Or I could create GeoJSON). I want to map them all on leaflet map.
How do I find what should I set as the center and as the zoom level so that all the points show up.
All the leaflet examples seem to use a fixed center and zoom, but I am accepting user input so I need to calculate them.
The easiest way is with a LatLngBounds object. You can then have the map fitBounds. Or get its center manually if you prefer.
var myPoints = [[1,1],[2,2] /*, ...*/ ];
var myBounds = new L.LatLngBounds(myPoints);
myMap.fitBounds(myBounds); //Centers and zooms the map around the bounds
You can even forgo instantiating the bounds object if you would like:
myMap.fitBounds([[1,1],[2,2] /*, ...*/ ]);
I'd recommend to use GeoJSON and create your leaflet map as described in the tutorial. Than you can simply use geojsonLayer.getBounds() together with map.setBounds() to zoom to your data.