I have a list of polylines, just like google maps does when I click on the polyline I want an infowindow to show up just where I clicked, and it works just fine with this function
function mapsInfoWindow(polyline, content) {
google.maps.event.addListener(polyline, 'click', function(event) {
infowindow.content = content;
infowindow.position = event.latLng;
infowindow.open(map);
});
}
the problem comes when I click on the list(using the same function for that), event obviously doesn't have the latLng, but I'd like infowindow to show up in the middle of the polyline anyway, just like it does when you click on the list in the google maps link I mentioned before.
Tried LatLngBounds(); but that gives the actuall center of the area the polylines create, not the middle I need.
Any idea how to do it?
So this is the(bit hacky) solution.
Use http://www.geocodezip.com/scripts/v3_epoly.js library, then count the total length of you polyline(various ways), divide it in half and call epoly's .GetPointsAtDistance() function upon it.
This should return LatLng point, but it acts a bit weird sometimes, returning two points or even turning that point somehow "broken". So the most secure thing you can do is probably this:
var pointInHalf = polyline.GetPointsAtDistance(polylineLength);
var pointCoordinate = new google.maps.LatLng(pointInHalf[0].lat(), pointInHalf[0].lng());
Well, better than nothing.
From http://www.geocodezip.com/v3_polyline_example_geodesic_proj.html
Without extensions and assuming the polyline is a straight line.
It is possible to convert the lat/lng coordinates to point plane (x,y) postions and calculate the average between the two. This will give you a central pixel position. You can then convert this position back to a latlng for map plotting.
var startLatLng = startMarker.getPosition();
var endLatLng = endMarker.getPosition();
var startPoint = projection.fromLatLngToPoint(startLatLng);
var endPoint = projection.fromLatLngToPoint(endLatLng);
// Average
var midPoint = new google.maps.Point(
(startPoint.x + endPoint.x) / 2,
(startPoint.y + endPoint.y) / 2);
// Unproject
var midLatLng = projection.fromPointToLatLng(midPoint);
var midMarker = createMarker(midLatLng, "text");
More information on changing the projection http://code.google.com/apis/maps/documentation/javascript/reference.html#Projection
So firstly you need to use the geometry library which calculates distances. Add libraries=geometry to your JS call, e.g.
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?libraries=geometry"></script>
Assuming you know the start point and end point for your polyline, you should be able to do this:
var inBetween = google.maps.geometry.spherical.interpolate(startLatlng, endLatlng, 0.5);
infowindow.position = inBetween;
I guess if you don't already know the start and end points, you could work it out from polyline.getPath().
to get the coordinates of your polyline you should do:
var widePath = new google.maps.Polyline({
path: waypointsCoordinates,
strokeColor: '#3366FF',
strokeOpacity: 0.0,
editable: true,
draggable: true,
strokeWeight: 3
});
and do:
var latLng [];
latLng = widePath.getPath().getArray();
Might be a bit old as well, but why not add the infobox on the click?
infowindow.setPosition(event.latLng);
infowindow.open(this.getMap());
If it's a click that is.
Related
This question (leaflet concentric circles (angular 2)) is similar, but since its not quite the same and no-one has answered it I thought I would start over.
My Leaflet map is populated by markers created in PHP from a MySql database. Here is a link if you want to see it; https://net-control.us/map1Test.php.
I would like to be able to right click on any given marker and have a series of concentric circles drawn around that marker at various distances in miles (example: 2, 5, 10, 15).
I think I need to do this with GeoJSON but any method that does what I need is OK with me. I just have not figured out how to first of all draw the circles at the distances I need and secondly how to allow a marker to be right clicked to display them.
I can't find any examples, and the only similar question has not been answered. Can someone show me how to do this? I use OSM maps.
The code (PHP) to make each marker looks like this;
$markers .= " var $callsign = new L.Marker(new L.LatLng($row[koords]),{ icon: new L.{$row[iconColor]}({number: '$rowno' }), title: \"marker_$rowno\"}).addTo(fg).bindPopup(\"$rowno<br>$row[mrkrfill]\").openPopup(); $($callsign._icon).addClass(\"$row[classColor]\"); markers.push($callsign); \n";
You don't share the code which creates the markers, so this is necessarily theoretical...
When creating the marker, add a contextmenu hook, which calls a function that uses L.circle to add circles to the map.
EDIT: So I peeked at the source of your site, your Javascript snippet for each marker should end up like this:
var WA0TJT = new L.Marker(new L.LatLng(39.202911,-94.602887),{
icon: new L.NumberedDivIcon({number: '1' }),
title:
"marker_1"}).addTo(fg).bindPopup("1<br><b>#0013</b><br>WA0TJT<br>Keith Kaiser<br>Platte Co., MO Dist: A<br>39.202911, -94.602887<br>EM29QE").openPopup().on("contextmenu", drawCircles);
$(WA0TJT._icon).addClass("bluemrkr");
markers.push(WA0TJT);
with a new function like this
function drawCircles(event e) {
L.circle(e.target.getLatLng(), {radius: <radius in metres>}).addTo(map);
// ... any more circles you need ...
}
var i;
var r = 1609.34; // in meters = 1 mile, 4,828.03 meters in 3 miles
var circleOptions = {
color: 'blue',
fillColor: '#69e',
fillOpacity: 0
}
for (i=0 ; i < 3; i++ ) {
r = (r * i) + r;
alert(lat+", "+lng);
var circle = L.circle([lat, lng], r, circleOptions);
circle.addTo(map);
r = 1609.34; // reset r so r calculation above works for each 1 mile step
}
I would like to know how to calculate the centre of a polygon created with this code from Mapbox: https://www.mapbox.com/mapbox.js/example/v1.0.0/show-polygon-area/
I would like to place a marker on the centre of the polygon after it's been created.
Thanks in advance.
To calculate the center of a polygon you first need to get it's bounds, that can be done using the getBounds method of L.Polygon which it enherits from L.Polyline:
Returns the LatLngBounds of the polyline.
http://leafletjs.com/reference.html#polyline-getbounds
It returns a L.LatLngBounds object which has a getCenter method:
Returns the center point of the bounds
http://leafletjs.com/reference.html#latlngbounds-getcenter
It returns a L.LatLng object which you can use to create a L.Marker:
var polygon = new L.Polygon(coordinates).addTo(map);
var bounds = polygon.getBounds();
var center = bounds.getCenter();
var marker = new L.Marker(center).addTo(map);
Or you can shorthand it:
var polygon = new L.Polygon(coordinates).addTo(map);
var marker = new L.Marker(polygon.getBounds().getCenter()).addTo(map);
Using that in the Mapbox example would look something like this:
function showPolygonArea(e) {
featureGroup.clearLayers();
featureGroup.addLayer(e.layer);
// Here 'e.layer' holds the L.Polygon instance:
new L.Marker(e.layer.getBounds().getCenter()).addTo(featureGroup);
e.layer.bindPopup((LGeo.area(e.layer) / 1000000).toFixed(2) + ' km<sup>2</sup>');
e.layer.openPopup();
}
You can use turf library.turf.center(features) gives you a point feature at the absolute center point of all input features. where features in your case will be the polygon selected which you can get using mapboxDraw.getAll()
I have a Google Map which contains a circle area.
I want to know if the circle is currently visible within the map's boundaries.
What I found so far is to check whether the center of the circle is within the boundaries, but I want to check for the whole circle and not only it's center.
My code that checks if the map's current center is within the boundaries of the circle is this:
google.maps.event.addListener(map, 'bounds_changed', function()
{
var circleBounds = circle.getBounds();
console.log(circleBounds.contains(map.getCenter()));
});
So what i want is something like this, which of course is not correct:
circleBounds.contains(map.getBounds());
Determine what the northmost, southmost, westmost, and eastmost LATLNGs of the circle are. You can find this given the circle radius
Determine if all points are inside the viewport bounds
If true, then yes, the circle must be viewable!
and you really should go back to your old questions and ACCEPT them (click on the check mark outline next to the best answer). This is how you show your appreciation for the hard work your answerers provided.
This is an old question - dinosaur-old in internet years - but if you're comparing two bounds, then the following holds a fortiori, n'est-ce pas?:
if(a.getBounds().contains(b.getBounds().getNorthEast())
&& a.getBounds().contains(b.getBounds().getSouthWest()))
{console.log('B is within A... Bounds are RECTANGLES: \
You only need to test two \
diagonally-opposing corners')};
That's because for a rectangle R, SW is (max(x),min(y)); NE is (min(x),max(y)) - and therefore all (x,y) in R are covered by the test.
I sure hope that's the case - it's how I do all my bounds comparisons...
Thanks Tina CG Hoehr.
Just in case anyone else wants this, here is the code for my question:
// Get the bounds
var circleBounds = circle.getBounds();
var ne = circleBounds.getNorthEast(); // LatLng of the north-east corner
var sw = circleBounds.getSouthWest();
var nw = new google.maps.LatLng(ne.lat(), sw.lng());
var se = new google.maps.LatLng(sw.lat(), ne.lng());
google.maps.event.addListener(map, 'bounds_changed', function()
{
var mapBounds = map.getBounds();
// Log whether the circle is inside or outside of the map bounds
if(mapBounds.contains(ne))
{
console.log("northeast is viewable");
}
if(mapBounds.contains(sw))
{
console.log("southwest is viewable");
}
if(mapBounds.contains(nw))
{
console.log("northwest is viewable");
}
if(mapBounds.contains(se))
{
console.log("southeast is viewable");
}
});
I have a map with various markers and i need to be able to draw a rectangle on the map and select the markers which are within the rectangle bounds.
So far i have found some great info here: How to get markers inside an area selected by mouse drag?
I have implemented the keymapzoom plugin ok. like so
$('#dispatcher').gmap3({action:'get'}).enableKeyDragZoom({
boxStyle: {
border: "dashed black",
//backgroundColor: "red",
opacity: 0.5
},
paneStyle: {
backgroundColor: "gray",
opacity: 0.2
}
});
var dz = $('#dispatcher').gmap3({action:'get'}).getDragZoomObject();
google.maps.event.addListener(dz, 'dragend', function (bnds) {
alert(bnds);
});
This gives me the following
((lat,long),(lat,long)) format from the alert(bnds);
I need to know how i can now check if any markers are within this?
I already have an object that is storing the markers for another reason. like:
markers[name] = {};
markers[name].lat = lati;
markers[name].lng = longi;
which might be useful?
I don't understand how to use the GLatLngBounds and containsLatLng(latlng:GLatLng) as suggested.
Your question is tagged with the v3 version of the Maps API, so I'll assume you are using that version (which you should as v2 is deprecated). Note that some classes and methods are named different than in your question.
Bounds are represented with the LatLngBounds class. You can perform the contains method on an instance of that class to determine if a point lies within those bounds.
If you have an object with all your markers, you can loop through them and check each marker, for example:
var bounds = new google.maps.LatLngBounds(sw, ne);
for (var a in markers) {
if (bounds.contains(new google.maps.LatLng(markers[a].lat, markers[a].lng)) {
// marker is within bounds
}
}
On a side note, I would store the LatLng object in the markers object when creating them. That way you don't have to create them wherever you need.
Box/Rectangle Draw Selection in Google Maps
This was my solution..
google.maps.event.addListener(dz, 'dragend', function(e) { //important listener
for(var i = 0; i < markers.length; i++){ // looping through my Markers Collection
if(e.contains(markers[i].position))
console.log("Marker"+ i +" - matched");
}
});
I'm drawing a series of markers on a map (using v3 of the maps api).
In v2, I had the following code:
bounds = new GLatLngBounds();
... loop thru and put markers on map ...
bounds.extend(point);
... end looping
map.setCenter(bounds.getCenter());
var level = map.getBoundsZoomLevel(bounds);
if ( level == 1 )
level = 5;
map.setZoom(level > 6 ? 6 : level);
And that work fine to ensure that there was always an appropriate level of detail displayed on the map.
I'm trying to duplicate this functionality in v3, but the setZoom and fitBounds don't seem to be cooperating:
... loop thru and put markers on the map
var ll = new google.maps.LatLng(p.lat,p.lng);
bounds.extend(ll);
... end loop
var zoom = map.getZoom();
map.setZoom(zoom > 6 ? 6 : zoom);
map.fitBounds(bounds);
I've tried different permutation (moving the fitBounds before the setZoom, for example) but nothing I do with setZoom seems to affect the map. Am I missing something? Is there a way to do this?
At this discussion on Google Groups I discovered that basically when you do a fitBounds, the zoom happens asynchronously so you need to capture the zoom and bounds change event. The code in the final post worked for me with a small modification... as it stands it stops you zooming greater than 15 completely, so used the idea from the fourth post to have a flag set to only do it the first time.
// Do other stuff to set up map
var map = new google.maps.Map(mapElement, myOptions);
// This is needed to set the zoom after fitbounds,
google.maps.event.addListener(map, 'zoom_changed', function() {
zoomChangeBoundsListener =
google.maps.event.addListener(map, 'bounds_changed', function(event) {
if (this.getZoom() > 15 && this.initialZoom == true) {
// Change max/min zoom here
this.setZoom(15);
this.initialZoom = false;
}
google.maps.event.removeListener(zoomChangeBoundsListener);
});
});
map.initialZoom = true;
map.fitBounds(bounds);
Anthony.
Without trying it, I'd say you should be able to do it just by having fitBounds() before you get the zoom level, i.e.
map.fitBounds(bounds);
var zoom = map.getZoom();
map.setZoom(zoom > 6 ? 6 : zoom);
If you did try that and it didn't work, you can setup your map with minZoom in the MapOptions (api-reference) like this:
var map = new google.maps.Map(document.getElementById("map"), { minZoom: 6 });
This would keep the map from zooming any further out when using fitBounds().
Anthony's solution is very nice. I only needed to fix the zoom for the inital page load (ensuring that you weren't too far zoomed in to start with) and this did the trick for me:
var zoomChangeBoundsListener =
google.maps.event.addListener(map, 'bounds_changed', function(event) {
google.maps.event.removeListener(zoomChangeBoundsListener);
map.setZoom( Math.min( 15, map.getZoom() ) );
});
map.fitBounds( zoomBounds );
You can also set the maxZoom option just before calling fitBounds() and reset the value afterwards:
if(!bounds.isEmpty()) {
var originalMaxZoom = map.maxZoom;
map.setOptions({maxZoom: 18});
map.fitBounds(bounds);
map.setOptions({maxZoom: originalMaxZoom});
}
When you call map.fitBounds() on one item - the map may zoom in too closely. To fix this, simply add 'maxZoom' to mapOptions...
var mapOptions = {
maxZoom: 15
};
In my case, I simply wanted to set the zoom level to one less than what google maps chose for me during fitBounds. The purpose was to use fitBounds, but also ensure no markers were under any map tools, etc.
My map is created early and then a number of other dynamic components of the page have an opportunity to add markers, calling fitBounds after each addition.
This is in the initial block where the map object is originally created...
var mapZoom = null;
Then this is added to each block where a marker is added, right before the map.fitBounds is called...
google.maps.event.addListenerOnce(map, 'bounds_changed', function() {
if (mapZoom != map.getZoom()) {
mapZoom = (map.getZoom() - 1);
map.setZoom(mapZoom);
}
});
When using 'bounds_changed' without the check in place, the map zoomed out once for every marker regardless of whether it needed it or not. Conversely, when I used 'zoom_changed', I would sometimes have markers under map tools because the zoom didn't actually change. Now it is always triggered, but the check ensures that it only zooms out once and only when needed.
Hope this helps.
Since Google Maps V3 is event driven, you can tell the API to set back the zoom to a proper amount when the zoom_changed event triggers:
var initial = true
google.maps.event.addListener(map, "zoom_changed", function() {
if (initial == true){
if (map.getZoom() > 11) {
map.setZoom(11);
initial = false;
}
}
});
I used initial to make the map not zooming too much when the eventual fitBounds is permorfed, but to let the user zoom as much as he/she wants. Without the condition any zoom event over 11 would be possible for the user.
I found the following to work quite nicely. It is a variant on Ryan's answer to https://stackoverflow.com/questions/3334729/.... It guarantees to show an area of at least two times the value of offset in degrees.
const center = bounds.getCenter()
const offset = 0.01
const northEast = new google.maps.LatLng(
center.lat() + offset,
center.lng() + offset
)
const southWest = new google.maps.LatLng(
center.lat() - offset,
center.lng() - offset
)
const minBounds = new google.maps.LatLngBounds(southWest, northEast)
map.fitBounds(bounds.union(minBounds))
I just had the same task to solve and used a simple function to solve it.
it doesn't care how many Markers are in the bounds - if there are a lot and the zoom is already far away, this zooms a little bit out, but when there is only one marker (or a lot very close to each other), then the zoomout is significant (customizable with the extendBy variable):
var extendBounds = function() {
// Extends the Bounds so that the Zoom Level on fitBounds() is a bit farer away
var extendBy = 0.005;
var point1 = new google.maps.LatLng(
bounds.getNorthEast().lat() + extendBy,
bounds.getNorthEast().lng() + extendBy
)
var point2 = new google.maps.LatLng(
bounds.getSouthWest().lat() - extendBy,
bounds.getSouthWest().lng() - extendBy
)
bounds.extend(point1);
bounds.extend(point2);
}
To use it, I use an own function to do the fitBounds():
map is my GoogleMap Object
var refreshBounds = function() {
extendBounds();
map.fitBounds(bounds);
}