leaflet place marker whenever position changes with watchposition? - javascript

In my Ionic app I would like to place a marker in the user position and change its position whenever the one of the user changes.
Is it possible to do that?
I thought I could do something like this:
let marker1;
let watch = this.geolocation.watchPosition();
watch.subscribe((data)=>{
var lat = data.coords.latitude
var lon = data.coords.longitude
let latlng = {lat: lat, lng: lon}
marker1 = L.marker(latlng)
let content1 = `<b>You are here</b>`;
marker1.bindPopup(content1)
marker1.addTo(this.map)
}, error => {
})
but I'm wondering if will be drawn on the map a series of markers for the different positions, instead of "overriding" the previous one.
I currently haven't tried it 'cause I can't simulate the change of position, but I was wondering if this is the right way to do that or I'm going completely wrong.

Related

Google Maps - get values from location array based on the map's current viewpoint

Edit:
Question = "is there a way to loop through the array and check if each location (long/lat) falls within the current viewport directly" (failing that get all markers within the viewport)
Background:
I have an array of locations (lat, long, id).
I want to:
On a Google Map, use the location array to display markers.
The user can scroll/zoom the map.
Have a button underneath the map, so when the user has decided on an area, he can click the button, and the code will return the ids (from the location array) that are contained within the viewport / map bounds.
There is a .contains for Google, so I guess you could potentially use that with something like
map.getBounds().contains and somehow reference each marker.getPosition()
but I wonder if there's a way to loop through the array and check if each location (long/lat) falls within the current viewport directly
You mean something like this (not tested), map is the google.maps.Map object and needs to be in scope. markersArray is the array of markers.
for (var i=0; i< markersArray.length; i++) {
if (map.getBounds().contains(markersArray[i].getPosition())) {
// the marker is in view
} else {
// the marker is not in view
}
}
http://jsfiddle.net/UA2g2/1/
Thanks geocodezip, you gave me the idea on how to solve it via looping through the array. I don't know if this is the most efficient way, but I put together some code that seems to do what I want - if you check the jsfiddle above and view console you can see that it logs when and which points are in the viewport.
$(document).ready(function(){
var myOptions = {
center: new google.maps.LatLng(51, -2),
zoom: 9,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
var storeArray = new Array(["51.38254", "-2.362804", "ID1"], ["51.235249", "-2.297804","ID2"], ["51.086126", "-2.910767","ID3"]);
google.maps.event.addListener(map, 'idle', function() {
for (i = 0; i < storeArray.length; i++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(storeArray[i][0], storeArray[i][1]),
map: map
});
}
for (var i=0; i<storeArray.length; i++) {
if (map.getBounds().contains(new google.maps.LatLng(storeArray[i][0], storeArray[i][1]))) {
console.log("marker: " + storeArray[i][2]);
}
}
});
});

Using custom markers in fusion-tables with address already geocoded locations

I was trying to follow this example http://code.google.com/p/gmaps-samples/source/browse/trunk/fusiontables/custom_markers.html?spec=svn2515&r=2515, to create custom markers.
I tried to change the example to use my data. The difference is that my data is already geocoded. I had trouble trying to figure why it didnt work when I changed the table id and the columns on the code.
So i printed the 'Address' on the original code and the one with my data.
The original code with the sample fusion-table, outputs the location like this
(37.4471132, -122.1602044)
Because my table is already geocoded I took away most of the function
function codeAddress(row) {
alert(row[1]);
var marker = new google.maps.Marker(
{
map : map,
position : row[1],
//this is where the magic happens!
icon : new google.maps.MarkerImage(icon: new google.maps.MarkerImage("http://www.google.com/images/icons/product/fusion_tables-32.png")
});
}
But the alert only diplays the coordinate a little bit different
<Point><coordinates>-78.423652,-0.203057,0.0</coordinates></Point>
So yeah, that is what I think it is not working
My opinion is that position : has to be followed by a google.maps.LatLng.
It looks like the row data is from KML, you need to extract the first two numbers to create the LatLng.
Mia DiLorenzo is right, the MarkerOption position expects a LatLng object.
Look at this example, which is very similar to yours, but it uses the Coordinates field to create the marker.
The example assumes, that the data in the Coordinates field is comma-separated "lat,lng"
e.g. 47.7672,-3.2022
But if your data happens to be in KML format then you can just extract the lat/lng values. The values are in order: longitude, latitude, and altitude (see the KML reference for details about KML coordinates):
function createLatLngObject(kmlString) {
//remove XML tags from input
var xmlRegEx = /<\/?\w+>/;
var kmlValue = kmlString.replace(xmlRegEx,'');
// now kmlValue contains e.g. -78.423652,-0.203057,0.0
//extract latitude and longitude
var coordinates = kmlValue.split(",");
var lat = coordinates[1];
var lng = coordinates[0];
return new google.maps.LatLng(lat, lng);
}
function createMarker(row) {
var latlng = createLatLngObject(row[1]);
var marker = new google.maps.Marker({
map: map,
position: latlng,
icon: new google.maps.MarkerImage("http://www.google.com/images/icons/product/fusion_tables-32.png")
});
}

google maps middle of a polyline (centroid?)

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.

Setting a Google Maps viewport to automatically fit pane for locations of (n) markers of various locations

The approach I took thus far has been:
function addMarker( query ) {
var geocoder = new google.maps.Geocoder();
var afterGeocode = $.Deferred();
// Geocode 'query' which is the address of a location.
geocoder.geocode(
{ address: query },
function( results, status ){
if( status === 'OK' ){
afterGeocode.resolve( results ); // Activate deferred.
}
}
);
afterGeocode.then( function( results ){
var mOptions = {
position: results[0].geometry.location,
map: map
}
// Create and drop in marker.
var marker = new google.maps.Marker( mOptions );
marker.setAnimation( google.maps.Animation.DROP );
var current_bounds = map.getBounds(); // Get current bounds of map
// use the extend() function of the latlngbounds object
// to incorporate the location of the marker
var new_bounds = current_bounds.extend( results[0].geometry.location );
map.fitBounds( new_bounds ); // fit the map to those bounds
});
}
The problem I'm running into is that the map inexplicably zooms out by some amount, no matter if the new marker fits within the current viewport or not.
What am I doing wrong?
ADDENDUM
I added logs and an additional variable to capture the map bounds after the transition was made (new_new_bounds)
current_bounds = // Map bounds before anything is done.
{-112.39575760000002, 33.60691883366427},
{-112.39295444655761, 33.639099}
new_bounds = // From after the extend
{-112.39295444655761, 33.60691883366427},
{-112.39575760000002, 33.639099}
new_new_bounds = // From after the fitbounds
{-112.33942438265382, 33.588697452015374},
{-112.44928766390382, 33.657309727063996}
OK, so after much wrangling, it turns out that the problem was a map's bounds are not the same as a map's bounds after fitBounds(). What happens (I presume), is Google takes the bounds you give it in the fitBounds() method, and then pads them. Every time you send the current bounds to fitBounds(), You're not going to fit bounds(x,y), you're going to fit bounds(x+m,y+m) where m = the arbitrary margin.
That said, the best approach was this:
var current_bounds = map.getBounds();
var marker_pos = marker.getPosition();
if( !current_bounds.contains( marker_pos ) ){
var new_bounds = current_bounds.extend( marker_pos );
map.fitBounds( new_bounds );
}
So, the map will only fit bounds if a marker placed falls outside the current map bounds. Hope this helps anyone else who hits this problem.
A possible explanation is that you randomly placed your new marker into the gap of the z-curve. A z-curve recursivley subdivide the map into 4 smaller tiles but that's also the reason why there are gaps between the tiles. A better way would be to use a hilbert curve or a moore curve for map applications. There is a patented search algorithm covering this issue, I think it is called multidimensional range query in quadtrees. You want to look for Nick's hilbert curce quadtree spatial index blog.

Google Maps v3: Enforcing min. zoom level when using fitBounds

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);
}

Categories

Resources