Can the default behavior of a Google Maps Event be overridden - google-maps

I am trying create a restriction on the movement of an editable polygon. I want to prevent the inside path from being pulled outside of outer path. I am using Google Maps google.maps.geometry.poly.containsLocation function to check if the new point is within the outer bounds but I can't find a way to stop the action from happening if it is outside.
I have tried returning false but the polygon still goes to the new location
google.maps.event.addListener(poly.getPaths().getArray()[1], 'set_at', function() {
return keepInside(this, arguments[0], arguments[1]);
});
function keepInside(polygon, index, new_point)
{
outer_edge = new google.maps.Polygon({
paths: polygon.getArray(),
});
is_in = google.maps.geometry.poly.containsLocation(new_point,outer_edge);
if(!is_in)
{
return false;
}
return true;
}

You got pretty close with your original code. Instead of returning false, you could set the point back to it's original position. Also, the second argument passed to the set_at listener is not the new point but the old point. Try this:
google.maps.event.addListener(poly.getPaths().getArray()[1], 'set_at', function(index, oldPoint) {
return keepInside(this, index, this.getAt(index), oldPoint);
});
function keepInside(polygon, index, newPoint, oldPoint)
{
outer_edge = new google.maps.Polygon({
paths: polygon.getArray(),
});
if(!google.maps.geometry.poly.containsLocation(newPoint, outer_edge))
{
polygon.setAt(index, oldPoint);
}
}

Related

Remove marker in google_maps_flutter

I have added Google Maps for Flutter
i know how to add a marker as it is given clearly in there examples
MarkerOptions _options = new MarkerOptions(
position: LatLng(
driver_lat,
driver_lng,
),
infoWindowText:
const InfoWindowText('An interesting location', '*'));
Marker marker = new Marker('1', _options);
//Adding Marker
googleMapController.addMarker(_options);
And i am removing the marker like below
googleMapController.removeMarker(marker);
for adding the marker it is taking MarkerOptions object as a parameter but for removing the marker it is asking for Marker object as parameter and my removing marker code is not working.
i am getting the below error
Failed assertion: line 201 pos 12: '_markers[marker._id] == marker': is not true.
There are two ways to do this, one is via clearMarkers() Method
mapController.clearMarkers();
Another one is via targeting each marker returned by mapController.markers
mapController.markers.forEach((marker){
mapController.removeMarker(marker);
});
2020 answer:
.clearMarkers() has been deprecated as now each Marker is a Widget stored in a map. The correct way to clear all of the markers now on your Google Map is to set the state of of your marker Map to an empty map.
e.g.
...
onPressed: () {
setState(() {
gMapMarkers = {};
});
}
....
Use clearMarkers(). It will clear all markers in your map. So try googleMapController.clearMarkers();
I've came across this issue myself with the google_maps_library and the main cause of this issue '_markers[marker._id] == marker': is not true. is the fact that all GoogleMapsController methods return a Future, so this error is, let's say a concurrency issue since the method cals are async.
The correct way to add/remove a marker would be:
_testRemoveMarker() async {
Marker marker = await _mapController.addMarker(...markerOption..);
_mapController.removeMarker(marker);
}
_clearMarkersAndRead() async {
_mapController.clearMarkers().then((_) {
//TODO: add makrers as you whish;
});
}
So, if you do any operations with the markers add/remove/update, you should be sure that the previous operation that involved markers is completed.
If anyone still struggling with removing a specific marker try this method;
MarkerId id = MarkerId("Pickup");
//markers[id] = {} as Marker; clear all markers
markers.removeWhere((key, value) => key == id); //clear a specific marker

How to get dragged point on the DirectionsRenderer

I use google maps for drawing my route. I setup only 2 points (start and end). The problem is that I will have to send the route to the next page.
I know that I can catch event:
google.maps.event.addListener(directionsDisplay, 'directions_changed', function() {
$("#full-route-json").val(encode64(JSON.stringify(directionsDisplay.directions.routes[0])));
});
But it will return all steps.
How to get the data only for my start and end points and dragged point like this:
AFAIK there is no implemented method to access these points(markers).
But these markers are stored inside a property of the DirectionsRenderer-instance.
The following code will give you these detail(please note that this code works now, but it may fail when the API changes)
google.maps.event.addListener(directionsDisplay, 'directions_changed', function() {
var that=this;
setTimeout(function(){//we need a short delay
for(var k in that){//iterate over all properties
if(typeof that[k].markers!='undefined'){//find the desired property
var markers=that[k].markers,arr=[];
for(var i=0;i<markers.length;++i){//collect the data
arr.push(markers[i].position.toString());
}
alert(arr.join(',\n'));
}
}},100)
});

Bring GoogleMaps InfoWindow to front

I have a GoogleMaps APIv3 application in which multiple InfoWindows can be open at any one time. I would like to be able to bring an obscured InfoWindow to the front of all other InfoWindows if any part of it is clicked - similar to the behaviour of windows in MS Windows OS.
I had thought to add an onclick event handler which increases the z-index of the InfoWindow, but the event handler does not appear to be firing.
ZIndex is a global variable that keeps increasing as InfoWindows are clicked - or thats the theory anyway.
Can anyone help ?
Here is my code:-
var ZIndex=1;
var iw = new google.maps.InfoWindow({ content:contentString });
google.maps.event.addListener(iw, 'click', handleInfoWindowClick(iw) );
function handleInfoWindowClick(infoWindow) {
return function() {
infoWindow.setZIndex(ZIndex++);
}
}
there is no click-event for an infoWindow, it's a little bit more difficult.
you'll need to use an element(not a string) as content for the infowindow, because you need a DOMListener instead a listener for the infowindow-object
when domready-fires, you must apply the click-DOMListener to the anchestor of this content-node that defines the infowindow
The following code will do this for you, add this to your page:
google.maps.InfoWindowZ=function(opts){
var GM = google.maps,
GE = GM.event,
iw = new GM.InfoWindow(),
ce;
if(!GM.InfoWindowZZ){
GM.InfoWindowZZ=Number(GM.Marker.MAX_ZINDEX);
}
GE.addListener(iw,'content_changed',function(){
if(typeof this.getContent()=='string'){
var n=document.createElement('div');
n.innerHTML=this.getContent();
this.setContent(n);
return;
}
GE.addListener(this,'domready',
function(){
var _this=this;
_this.setZIndex(++GM.InfoWindowZZ);
if(ce){
GM.event.removeListener(ce);
}
ce=GE.addDomListener(this.getContent().parentNode
.parentNode.parentNode,'click',
function(){
_this.setZIndex(++GM.InfoWindowZZ);
});
})
});
if(opts)iw.setOptions(opts);
return iw;
}
Instead of google.maps.InfoWindow() you must call now google.maps.InfoWindowZ()
It also returns a genuine InfoWindow, but with the mentioned listener applied to it. It also creates the node from the content when needed.
Demo: http://jsfiddle.net/doktormolle/tRwnE/
Updated version for visualRefresh(using mouseover instead of click) http://jsfiddle.net/doktormolle/uuLBb/

Google Maps KMZ file not rendering in IE8 and IE7

I have a web app with a map in it. I've added a nice little custom map control to turn on and off different layers on the map. Currently there are only two layers, and it all works nice and fine in most browsers.
Except for IE8+7. None of the layers are showing on the map when turned on. As far as I can tell the map is loading the kmz/kml files (when preserveViewport is set to false, the map moves to the right location) but they're just not appearing. One layer contains polylines, and the other contains markers. The code I use is below:
function someFunction() {
//code to initialise map etc goes here...
var layers = [];
//Create 1st layer
var exchangeslayer = new google.maps.KmlLayer('http://link.to.file/exchanges.kmz'
suppressInfoWindows: true,
preserveViewport: true
});
layers.push({name: "Exchanges", layer: exchangeslayer});
//Code to create second layer
var nyclayer = new google.maps.KmlLayer('http://www.nyc.gov/html/dot/downloads/misc/cityracks.kml'
suppressInfoWindows: true,
preserveViewport: false
});
layers.push({name: "NY City Tracks", layer: nyclayer});
addCustomLayerControls(layers);
}
function addCustomLayerControls(layers) {
//there is code here that would generate the divs for the custom map control
var container; //container is a div element created via javascript
for (var i = 0; i < layers.length; i++) {
this.addLayerLabelToContainer(layers[i], container);
}
//some more code
}
function addLayerLabelToContainer(layer, container) {
var map; //Assume I get a reference to the map
//some code here to make pretty labels for the map controls...
var layerLabel; // layerLabel is a div element created via javascript
google.maps.event.addDomListener(layerLabel, 'click', function() {
if(layer.layer.map == null) {
layer.layer.setMap(map);
} else {
layer.layer.setMap(null);
}
});
}
So as it turns out my problem related to CSS. One of my stylesheets was applying max-width: 100% to all img tags. This was playing havok with the map markers/polylines.
Its obvious now that I see it, but when you think the problem is to do with the javascript its not so obvious. As such, I'll leave this answer here for anyone else who makes the same mistake as me.
If you modify addLayerLabelToContainer() like this then it works in IE as expected. Verified it loads KMZ correctly in IE 8 and 9.
function addLayerLabelToContainer(layer, container) {
// var map; //Assume I get a reference to the map
//some code here to make pretty labels for the map controls...
var layerLabel; // layerLabel is a div element created via javascript
if(layer.layer.map == null) {
layer.layer.setMap(map);
} else {
layer.layer.setMap(null);
}
}
Don't need to invoke addDomListener(). Also note the API syntax:
addDomListener(instance:Object, eventName:string, handler:Function)
Also minor fix of syntax errors in someFunction as follows:
function someFunction() {
// var map; //assume map is initialised, I've just removed that code
var layers = [];
// see https://developers.google.com/maps/documentation/javascript/layers
//Create 1st layer
var exchangeslayer = new google.maps.KmlLayer(
'http://kml-samples.googlecode.com/svn/trunk/kml/kmz/simple/big.kmz',
{ suppressInfoWindows: true, preserveViewport: true
});
layers.push( {name: "Exchanges", layer: exchangeslayer} );
// ...
addCustomLayerControls(layers);
}

Google Maps v3 OverlayView.getProjection()

I cannot seem to figure out why the object returned by getProjection() is undefined. Here is my code:
// Handles the completion of the rectangle
var ne = recBounds.getNorthEast();
var sw = recBounds.getSouthWest();
$("#map_tools_selat").attr( 'value', sw.lat() );
$("#map_tools_nwlat").attr( 'value', ne.lat() );
$("#map_tools_selng").attr( 'value', ne.lng() );
$("#map_tools_nwlng").attr( 'value', sw.lng() );
// Set Zoom Level
$("#map_tools_zoomlevel").attr( 'value', HAR.map.getZoom()+1 );
document.getElementById("map_tools_centerLat").value = HAR.map.getCenter().lat();
document.getElementById("map_tools_centerLong").value = HAR.map.getCenter().lng();
// All this junk below is for getting pixel coordinates for a lat/lng =/
MyOverlay.prototype = new google.maps.OverlayView();
MyOverlay.prototype.onAdd = function() { }
MyOverlay.prototype.onRemove = function() { }
MyOverlay.prototype.draw = function() { }
function MyOverlay(map) { this.setMap(map); }
var overlay = new MyOverlay(HAR.map);
var projection = overlay.getProjection();
// END - all the junk
var p = projection.fromLatLngToContainerPixel(recBounds.getCenter());
alert(p.x+", "+p.y);
My error is: Cannot call method 'fromLatLngToContainerPixel' of undefined
Actually, i the reason why this happens is because the projection object is created after the map is idle after panning / zooming. So, a better solution is to listen on the idle event of the google.maps.Map object, and get a reference to the projection there:
// Create your map and overlay
var map;
MyOverlay.prototype = new google.maps.OverlayView();
MyOverlay.prototype.onAdd = function() { }
MyOverlay.prototype.onRemove = function() { }
MyOverlay.prototype.draw = function() { }
function MyOverlay(map) { this.setMap(map); }
var overlay = new MyOverlay(map);
var projection;
// Wait for idle map
google.maps.event.addListener(map, 'idle', function() {
// Get projection
projection = overlay.getProjection();
})
I kind of figured out what was going on. Even though it is still not crystal clear why this happens, I know that I had to instantiate the variable "overlay" right after instantiating my google map (HAR.map). So I practically moved that code snippet into my HAR class and now i use:
HAR.canvassOverlay.getProjection().fromLatLngToContainerPixel( recBounds.getCenter() );
So now, every time I create a map via my class "HAR" I also have a parallel OverlayView object within my class.
The Error could have been with losing scope of my class object, but I think it was more of the map event "projection_changed" not being fired. I got a hint from the map API docs for map class, under method getProjection():
"Returns the current Projection. If the map is not yet initialized (i.e. the mapType is still null) then the result is null. Listen to projection_changed and check its value to ensure it is not null."
If you are getting the similar issue, make sure that you assign your overlayView.setMAP( YOUR_MAP_OBJECT ) closely after instantiating the map object.