Google Maps v3 OverlayView.getProjection() - google-maps

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.

Related

Flutter update element create by ui.platformViewRegistry.registerViewFactory class

I am working with Google Map for flutter Website. As most tutorial said that, the map is displayed through ui.platformViewRegistry.registerViewFactory
below is the code example. As you see, i create the widget with the variable and tried to give the _lat and _lng from outside. But after i tried to give the value, the ui.platformViewRegistry.registerViewFactory is not triggered.
I just realize that the ui.platformViewRegistry.registerViewFactory objected to create the element so when the element was created, it will not executed again, but I cannot access the element via document.getElementById('map-canvas') either.
Anyone have idea about this?
Widget getMap(double _lat, double _lng) {
String htmlId = "map-canvas";
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
// final myLatLng = LatLng(-25.363882, 131.044922);
final mapOptions = new MapOptions()
..zoom = 8
..center = new LatLng(_lat, _lng)
..mapTypeControl = false;
final elem = DivElement()
..id = htmlId
..style.width = "100%"
..style.height = "100%"
..style.border = "none";
final map = new GMap(elem, mapOptions);
Marker(MarkerOptions()
..position = LatLng(_lat, _lng)
..map = map
..title = 'Green Energy');
return elem;
});
return HtmlElementView(
viewType: htmlId,
);
}
I found two ways to solve this problem.
It will update the whole view when you change the htmlId and reload.
Either determine how the htmlId should change or give it a new random number with every reload. String htmlId = Random().nextInt(1000).toString();
The disadvantage here is that you always reload the whole map.
This is the better solution as you dont have to reload the complete map. I also had the problem that i could change values inside the ui.platformViewRegistry.registerViewFactory function with data i give to the getMap widget.
So the first part of this solution is to create a global variable that you use inside the ui.platformViewRegistry.registerViewFactory function but update outside.
The second part is to use listener functions inside the ui.platformViewRegistry.registerViewFactory. For example map.onClick.listen((mapsMouseEvent) {}); or map.onZoomChanged.listen((_) {}); or a Stream ANY_STREAM_CONTROLLER.stream.listen((data) {}).
If you would like to add new markers when you zoom out you could do it something like this
ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
map.onZoomChanged.listen((_) {
List<LatLng> marker_list = list_of_markers(map.zoom, map.center);
//Function that gives you a list of markers depending on your zoom level and center
marker_list.forEach((latlng){
Marker marker = Marker(MarkerOptions()
..position = latlng
..map = map
..title = myLatlng.toString()
..label = myLatlng.toString());
});
});
}
I hope the solution helps you

Google Maps Data Layer, Click on geometry programmatically

I have a google map where I load geometry data to the data layer using geoJSON. I bind to the following event in the data layer.
map.data.addListener('click', function (event) { console.log(event);});
How can I trigger this event manually? I know I can trigger the click event on a marker manually, but it is triggered through google.maps.event.
That should be done with google.maps.event.trigger.
Try this (not sure):
// invoke a click
google.maps.event.trigger(map.data, 'click');
While Emmanuel's answer is technically correct, I would like to expand on it because I struggled for 2-3 hours to get this working.
In my code, I have a predefined click event function which was using feature object.
Here is my click event where I have used feature object:
transLayerData.addListener('click', function (event) {
var lfeature = event.feature;
var html = lfeature.getProperty('popupInfo');
infowindow.setContent(html);
infowindow.setPosition(event.latLng);
infowindow.setOptions({pixelOffset: new google.maps.Size(0,-34)});
infowindow.open(myMap);
});
If you are using a feature object in your predefined data layer click function (like above) , it is important that you pass the feature object and you create an event object with feature object in it.
Here is my code which gets the lat and long out of feature object and creates an event object:
var featureGeometry = feature.getGeometry();
var lsType = featureGeometry.getType();
var isLineData = false;
var lsType = featureGeometry.getType();
if ((lsType == 'LineString') || (lsType == 'MultiLineString') || (lsType == 'LinearRing') || (lsType == 'Polygon') | (lsType == 'MultiPolygon')) {
isLineData = true;
}
var featurePosition;
if (isLineData) {
// will center the map on the first vertex of the first LineString
var tmp = featureGeometry.getAt(0);
featurePosition = featureGeometry.getAt(0);
// following will set line's storke weight to 10
feature.setProperty('strokeWeight', 10);
} else{
featurePosition = featureGeometry.get();
}
myMap.setZoom(10);
myMap.setCenter(featurePosition);
var llat = featurePosition.lat();
var llong = featurePosition.lng();
// Creating event with the feature object is important. Especially if you have your very own feature click method defined
var lEvent = {
stop: null,
latLng: new google.maps.LatLng(llat, llong),
feature: feature,
}
// transLayerData is your data layer object.
// This is how I have defined my translayer object:
// transLayerData = new google.maps.Data({ map: myMap });
if (!isLineData) {
google.maps.event.trigger(transLayerData, 'click', lEvent);
} else {
google.maps.event.trigger(transLayerData, 'mouseover', lEvent);
}
}

Get non-wrapping map bounds in Google Maps API V3

If you zoom a google map out the world will start to repeat horizontally. Using .getBounds() seems to return the longitude at the edges of the displayed map image. But I would like to get minimum and maximum longitudes for the current view of the real world.
For example in this image .getBounds() says that the longitude ranges between 116 and 37 degrees (giving a width of -79 degrees!). The range I'm looking for is -244 to +37.
(or even -180 to +37 as this is the extremes of the world that is viewable around the map centre.)
And another example. Here I'm looking for -180 to +180 ...
You can try it for yourself here...
http://jsfiddle.net/spiderplant0/EBNYT/
(Apologies if this has been answered before - I did find some old similar questions but none seemed to have satisfactory answers).
I ran into the same issues today, but I think I finally figured it out.
In the first scenario above, you can use map.getBounds().toSpan() to get the width in longitude.....as long as the map did not wrap around.
For the second scenario where the map wraps around, I extended the google.maps.OverlayView() to get the google.maps.MapCanvasProjection object. From there you can call the function getWorldWidth().
It will give you the world width in pixel, then you can compare it with your map container's width. If your container is bigger, your map has wrapped around.
Don't know if the function is meant for this but it works.
The answer proposed by user1292293 worked for me (Google map api V3)
Extension of google.maps.OverlayView()
function MyMapOverlay() {}
MyMapOverlay.prototype = new google.maps.OverlayView();
MyMapOverlay.prototype.onAdd = function() {};
MyMapOverlay.prototype.draw = function() {};
MyMapOverlay.prototype.onRemove = function() {};
Add overlay to the map
var map = new google.maps.Map(domContainer, {...});
var overlay = new MyMapOverlay();
overlay.setMap(map);
check if map wraps around:
var proj = overlay.getProjection();
var wwidth = 0;
if (proj) wwidth = proj.getWorldWidth();
var mapsWrapsAround=false;
if (__wwidth > 0 && __wwidth < domContainer.width()) mapsWrapsAround = true;
I used the answer from rebpp to prevent the map from wrapping by setting the getWorldWidth. Here's the MapWrappingPrevent I created.
To use this just call
var wrapPreventer = new MapWrappingPrevent(_map);
/* This class prevents wrapping of a map by adjusting the max-width */
function MapWrappingPrevent(map) {
var self = this;
this.setMap(map);
map.addListener('zoom_changed', function () {
self.onZoomChanged();
});
}
MapWrappingPrevent.prototype = new google.maps.OverlayView();
MapWrappingPrevent.prototype.onAdd = function () { this.onZoomChanged(); };
MapWrappingPrevent.prototype.draw = function () { };
MapWrappingPrevent.prototype.onRemove = function () { };
MapWrappingPrevent.prototype.onZoomChanged = function () {
var proj = this.getProjection();
if (proj) {
var wrappingWidth = proj.getWorldWidth();
$(this.getMap().getDiv()).css({'max-width': wrappingWidth + 'px'})
}
};

google map v3 how fitbounds with zoom on user's location

I am using google v3, i want to fitbounds with center on userPinLoc object, i have the following code
var bounds = new google.maps.LatLngBounds();
bounds.extend(userPinLoc)// wants to center on userPinLocation
for (i in nearestEntitiesToZoom) {
entity = nearestEntitiesToZoom[i];
var googleLatLng = new google.maps.LatLng(entity.latitude,entity.longitude);
bounds.extend(googleLatLng);
}
bounds.extend(userPinLoc);
//googleMap.setCenter(userPinLoc) this not working
googleMap.fitBounds(bounds);
any quick fix after update i am pasting new code
function setInitialZoom() {
mapZoom = googleMap.getZoom();
var bounds = new google.maps.LatLngBounds();
bounds.extend(userPinLoc);
for (i in nearestEntitiesToZoom) {
entity = nearestEntitiesToZoom[i];
var googleLatLng = new google.maps.LatLng(entity.latitude,entity.longitude);
bounds.extend(googleLatLng);
}
google.maps.event.addDomListener(googleMap, 'bounds_changed', function() {
googleMap.setCenter(userPinLoc);
});
googleMap.fitBounds(bounds);
setTimeout(function() {
google.maps.event.clearListeners(googleMap, 'bounds_changed');
}, 3000);
}
Remove the setCenter from where it is currently. You need to have an event listener for when the map's bounds change. I think when you call fitBounds, you have to wait for it to redraw before you can adjust the centre. One way would be to use a timeout, but you can simply add this to your initialize function:
google.maps.event.addDomListener(googleMap, 'bounds_changed', updateCenter);
And then create a new function to update the centre, which takes the userPinLoc value (needs to be a global variable):
function updateCenter() {
googleMap.setCenter(userPinLoc);
}

Google Maps v3: check if point exists in polygon

I am looking to find a way of checking if a point exists inside a polygon in Google Maps v3 (JavaScript). I've searched everywhere and the only solutions I have found so far have been to do with getting the bounds of the polygon, but the code shown seems to just create a rectangle and keeps expanding its surface area to include all relevant points.
By the way, the reason I can't just use a big square i.e. getting a polygons bounds, is that I have bordering polygons on the map and they can not expand into each other's territory.
EDIT
Following on from the reply below, I have tried implementing the example code using one of my existing polygons but it is just saying that it is not defined and I can't figure out why.
Here is my declaration:
myCoordinates = [
new google.maps.LatLng(0.457301,-0.597382),
new google.maps.LatLng(0.475153,-0.569916),
new google.maps.LatLng(0.494379,-0.563049),
new google.maps.LatLng(0.506738,-0.553436),
new google.maps.LatLng(0.520470,-0.541077),
new google.maps.LatLng(0.531456,-0.536957),
new google.maps.LatLng(0.556174,-0.552063),
new google.maps.LatLng(0.536949,-0.596008),
new google.maps.LatLng(0.503991,-0.612488),
new google.maps.LatLng(0.473780,-0.612488) ];
polyOptions = {
path: myCoordinates,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#0000FF",
fillOpacity: 0.6 };
var rightShoulderFront = new google.maps.Polygon(polyOptions);
rightShoulderFront.setMap(map);
and here is where I am checking for the point:
var coordinate = selectedmarker.getPosition();
var isWithinPolygon = rightShoulderFront.containsLatLng(coordinate);
console.log(isWithinPolygon);
But it keeps coming up with the error: Uncaught ReferenceError: rightShoulderFront is not defined
One algorithm to solve this is ray-casting. See an explanation here.
And you can find code implementing this for the Google Maps JS API V3 here.
HTH.
You can do this quite simply with Google maps geometry library.
First be sure to add the google maps geometry library.
<script type="text/javascript" src="//maps.googleapis.com/maps/api/js?libraries=geometry&sensor=false"></script>
Then, define your polygon
var rightShoulderFront = new google.maps.Polygon({
paths: myCoordinates
});
rightShoulderFront .setMap(map);
I'm going to add an event listener to handle a 'click' event, but you can adapt to fit your needs
google.maps.event.addListener(rightShoulderFront , 'click', isWithinPoly);
Create a function to handle our click event an check if coordinate exists within polygon using Google's geometry library
/** #this {google.maps.Polygon} */
function isWithinPoly(event){
var isWithinPolygon = google.maps.geometry.poly.containsLocation(event.latLng, this);
console.log(isWithinPolygon);
}
You have a very good example of containsLocation() method in Google Maps API documentation.
You should have a look about the Gmaps.js library. It has a quite simple method about geofence.
var coordinate = new google.maps.LatLng(0.457301,-0.597382);//replace with your lat and lng values
var isWithinPolygon = google.maps.geometry.poly.containsLocation(coordinate, yourPolygon);
Dont forget to include the library in your googleapis script. Read more...
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=geometry"></script>
The example and implementation do not take into account that a polygon can cross the 180 degrees boundary.
The implementation does take it into account (implicitly) in the bounding box check, but the polygon check fails.
I have used the same thing and working fine and its offline code I have written this code in PHP you can write it any programming language.
class pointLocation {
var $pointOnVertex = true; // Check if the point sits exactly on one of the vertices?
function pointLocation() {
}
function pointInPolygon($point, $polygon, $pointOnVertex = true) {
$this->pointOnVertex = $pointOnVertex;
// Transform string coordinates into arrays with x and y values
$point = $this->pointStringToCoordinates($point);
$vertices = array();
foreach ($polygon as $vertex) {
$vertices[] = $this->pointStringToCoordinates($vertex);
}
// Check if the point sits exactly on a vertex
if ($this->pointOnVertex == true and $this->pointOnVertex($point, $vertices) == true) {
return "vertex";
}
// Check if the point is inside the polygon or on the boundary
$intersections = 0;
$vertices_count = count($vertices);
for ($i=1; $i < $vertices_count; $i++) {
$vertex1 = $vertices[$i-1];
$vertex2 = $vertices[$i];
if ($vertex1['y'] == $vertex2['y'] and $vertex1['y'] == $point['y'] and $point['x'] > min($vertex1['x'], $vertex2['x']) and $point['x'] < max($vertex1['x'], $vertex2['x'])) { // Check if point is on an horizontal polygon boundary
return "boundary";
}
if ($point['y'] > min($vertex1['y'], $vertex2['y']) and $point['y'] <= max($vertex1['y'], $vertex2['y']) and $point['x'] <= max($vertex1['x'], $vertex2['x']) and $vertex1['y'] != $vertex2['y']) {
$xinters = ($point['y'] - $vertex1['y']) * ($vertex2['x'] - $vertex1['x']) / ($vertex2['y'] - $vertex1['y']) + $vertex1['x'];
if ($xinters == $point['x']) { // Check if point is on the polygon boundary (other than horizontal)
return "boundary";
}
if ($vertex1['x'] == $vertex2['x'] || $point['x'] <= $xinters) {
$intersections++;
}
}
}
// If the number of edges we passed through is odd, then it's in the polygon.
if ($intersections % 2 != 0) {
return "inside";
} else {
return "outside";
}
}
function pointOnVertex($point, $vertices) {
foreach($vertices as $vertex) {
if ($point == $vertex) {
return true;
}
}
}
function pointStringToCoordinates($pointString) {
$coordinates = explode(" ", $pointString);
return array("x" => $coordinates[0], "y" => $coordinates[1]);
}
}
$pointLocation = new pointLocation();
$points = array("22.732965336387213 75.8609390258789");
$polygon = array("22.73549852921309 75.85424423217773","22.72346544538196 75.85561752319336","22.72346544538196 75.87175369262695","22.732332030848273 75.87295532226562","22.740406456758326 75.8686637878418","22.74198962160603 75.85407257080078");
echo '<pre>';
print_r($polygon);
// The last point's coordinates must be the same as the first one's, to "close the loop"
foreach($points as $key => $point) {
echo "point " . ($key+1) . " ($point): " . $pointLocation->pointInPolygon($point, $polygon) . "<br>";
}
?>
I think google has solved it with this method of containsLocation()
https://developers.google.com/maps/documentation/javascript/examples/poly-containsLocation