Google Maps: Polygon and Marker Z-Index - javascript

I have a Google Map with many markers (yellow circles), and I implemented a tool to draw polygons over the markers. However, the polygon is behind the markers while drawing (and stays behind when complete).
I tried changing the ZIndex in both markers and polygons, but it seems to alter the way in which markers are shown with respect to other markers, and not with respect to polygons. I also tried
polygon.setZIndex(google.maps.Marker.MAX_ZINDEX + 1);
How can I bring the polygon to the front?

This won't solve the problem, but it will explain why the things you tried didn't work.
The Maps API uses several layers known as MapPanes in a fixed Z order:
4: floatPane (infowindow)
3: overlayMouseTarget (mouse events)
2: markerLayer (marker images)
1: overlayLayer (polygons, polylines, ground overlays, tile layer overlays)
0: mapPane (lowest pane above the map tiles)
So the marker images in layer 2 are always above the polygons in layer 1. When you fiddle with the z-index on the markers, you're just adjusting them relative to each other. That doesn't do any good, because they are all in a layer above the polygons.
What can you do about this? The only solution I can think of is to create your own OverlayView for the polygons or the markers so you can put them in the MapPane you want.
Are your markers clickable, or are they just static images? If they aren't clickable, you could possibly get away with drawing them yourself in the mapPane. Then your polygons would be above them. Or the opposite: you could draw the polygons yourself in one of the higher layers, maybe in floatShadow.
The problem then is you have to do all of your own drawing, either with a canvas element or with DOM images. And your own mouse hit testing too if they are clickable.
There aren't a lot of good OverlayView examples out there, but I'll mention one of my own: a little library I wrote a while ago called PolyGonzo, where the polygonzo.js file has the OverlayView implementation. The code isn't great - I threw it together in too much of a hurry - but it may help give you some ideas.

I know this question is old but for future users I wanna share my approach:
Shapes with higher zIndex values displaying in front of those with lower values. For this example I am using Polygon but is similar for other shapes:
var globalZIndex = 1; //Be sure you can access anywhere
//... Other instructions for creating map, polygon and any else
polygon.setOptions({ zIndex: globalZIndex++ });
Notice that markers have a method setZIndex(zIndex:number).

I found this solution
To Create a Symbol use this code below
var lineSymbol = {
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
strokeColor: '#181727',
fillColor: '#50040B',
};
var dashedSymbol = {
path: 'M 0,-1 0,1',
strokeOpacity: 1,
scale: 4
};
[![function MakeMarker(pinColor){
var pinImage = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor,
new google.maps.Size(21, 34),
new google.maps.Point(0,0),
new google.maps.Point(10, 34));
return pinImage;
}][1]][1]
FlowMarkersdashed(new google.maps.LatLng(positionorigin[0], positionorigin[1]),
new google.maps.LatLng(positiondestination[0], positiondestination[1]), myObject[i]['flowfluxphysique'][j]['colorFlux'], dashedSymbol, j);
function FlowMarkersdashed(latlngOrgin, latlngDest, ColorFlow, Symbol, indexvar){
var flightPlanCoordinates = [
latlngOrgin,
{lat: latlngOrgin.lat() + (indexvar) * 2, lng: latlngOrgin.lng()},
// {lat: -18.142, lng: 178.431},
latlngDest,
];
var line = new google.maps.Polyline({
path: flightPlanCoordinates,
strokeOpacity: 0,
icons: [{
icon: Symbol,
// offset: '100%',
offset: '0',
repeat: '20px'
// repeat: '20px'
}],
strokeColor: "#"+ColorFlow,
geodesic: true,
// editable: true,
map: map
});
}
And to Create a Flow Marker try this code
FlowMarkers(new google.maps.LatLng(positionorigin[0], positionorigin[1]),
new google.maps.LatLng(positiondestination[0], positiondestination[1]), myObject[i]['flowfluxinformation'][j]['colorFlux'], lineSymbol,j);
function FlowMarkersdashed(latlngOrgin, latlngDest, ColorFlow, Symbol, indexvar){
var flightPlanCoordinates = [
latlngOrgin,
{lat: latlngOrgin.lat() + (indexvar) * 2, lng: latlngOrgin.lng()},
// {lat: -18.142, lng: 178.431},
latlngDest,
];
var line = new google.maps.Polyline({
path: flightPlanCoordinates,
strokeOpacity: 0,
icons: [{
icon: Symbol,
// offset: '100%',
offset: '0',
repeat: '20px'
// repeat: '20px'
}],
strokeColor: "#"+ColorFlow,
geodesic: true,
// editable: true,
map: map
});
}
This is my result

Change this method call:
polygon.setZIndex(google.maps.Marker.MAX_ZINDEX + 1);
to this:
polygon.setZIndex(4);

Related

How to change the default Marker used in the Google Maps API Drawing Layer controls

I would like to know how could I use a custom marker with google maps drawing controls. I want to do that because I need to place a marker when a user clicks in the map and have it open a info window (when clicked) and have a few custom actions in there (buttons and such).
I'm using react and #react-google-maps/api for this, but that might be besides the point since it is just a wrapper around the Maps Javascript API provided by Google.
From the docs it is possible to provide google.maps.MarkerOptions to the google.maps.drawing.DrawingManagerOptions. Unfortunately there is no option there to provide a custom Marker to be rendered.
I tried using the markercomplete() call back exposed by the google.maps.drawing.DrawingManager since it has the newly created Marker as parameter, and then doing something like this:
const handleNewMarker = (marker) => {
marker.addListener('click', function() {
setActiveMarker(marker);
});
}
My map component would then be something like this:
<GoogleMap
zoom={18}
center={latLng}
>
{activeMarker && <CustomInfoWindow anchor={activeMarker} />}
<DrawingManager
options={{
markerOptions: {
clickable: true,
draggable: true,
},
}}
onMarkerComplete={handleNewMarker}
/>
</GoogleMap>
Although this worked, it is not at all viable for production, for some reason the InfoWindow takes too much time to appear in the screen, that approach might be causing a memory leak and I don`t know why.
I might be missing something here, but on my research I didn't find a single soul trying to create a custom marker with the drawing tool, just custom markers by themselves which is relatively easy to do. My ideal case scenario, since I'm using React, would be to create a CustomMarker component with a CustomInfoWindow inside it, and just tell the drawing controls, "hey, take this marker and use it whenever a user tries to draw a new marker with your drawing tool".
Thank you.
Edit
Here is a screenshot of what I mean, that marker in the screen shot was placed there using the "new marker" drawing control, and I need the "new marker" drawing control to place a custom marker defined by me.
All you need to do is to set the icon property in the MarkerOptions, if I correctly understand your meaning of "custom marker". Below is an example using a SVG path for the icon.
This snippet is in full JS but the same should work with the React library.
var map;
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(-34.397, 150.644),
zoom: 8
};
map = new google.maps.Map(document.getElementById('map'), mapOptions);
var drawingManager = new google.maps.drawing.DrawingManager({
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [google.maps.drawing.OverlayType.MARKER]
},
markerOptions: {
draggable: false,
icon: {
path: "M-20,0a20,20 0 1,0 40,0a20,20 0 1,0 -40,0",
fillColor: '#FF0000',
fillOpacity: 0.6,
anchor: new google.maps.Point(0, 0),
strokeWeight: 0,
scale: 1
}
}
});
drawingManager.setMap(map);
}
#map {
height: 180px;
}
<div id="map"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initialize&libraries=drawing" async defer></script>
Now if you need the marker to be clickable, have an Infowindow, etc. do you really need to use the drawing manager? Could you not simply listen for a map click event, and create a standard marker?
Or possibly, use both? (Create the marker with the drawing manager and convert it to a standard marker by the use of the markercomplete event, which seems to be more or less what you are doing.)
Edit:
If you need to create a "real" marker with an InfoWindow, you can do it in the markercomplete event.
var map;
var infowindow;
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(-34.397, 150.644),
zoom: 8
};
map = new google.maps.Map(document.getElementById('map'), mapOptions);
infowindow = new google.maps.InfoWindow();
var drawingManager = new google.maps.drawing.DrawingManager({
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: [google.maps.drawing.OverlayType.MARKER]
}
});
drawingManager.setMap(map);
google.maps.event.addListener(drawingManager, 'markercomplete', function(marker) {
// Remove overlay from map
marker.setMap(null); // Optional, but this will remove the drawn marker
drawingManager.setDrawingMode(null); // Optional, but this will "disable" the drawing tools
// Create the "real" marker
createMarker(marker.getPosition());
});
}
function createMarker(position) {
var marker = new google.maps.Marker({
position: position,
map: map,
title: 'Custom marker',
icon: {
path: "M-20,0a20,20 0 1,0 40,0a20,20 0 1,0 -40,0",
fillColor: '#FF0000',
fillOpacity: 0.6,
anchor: new google.maps.Point(0, 0),
strokeWeight: 0,
scale: 1
}
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent('This is the content');
infowindow.open(map, this);
});
}
#map {
height: 180px;
}
<div id="map"></div>
<!-- Replace the value of the key parameter with your own API key. -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initialize&libraries=drawing" async defer></script>

Drawing svg circles with Google Maps

I'm working on a site that wants to use draw some circles to represent points of interest on a Google Map, but haven't found a way to draw a proper SVG circle element on the map.
The Google documentation outlines a circle element but these render as polygons rather than circles (take a look at the borders of the circle and you'll see they have a strange polygon fit to a rough circle shape, rather than a raw svg:circle element).
Is it possible to draw a true SVG circle with things like a r attribute for radius? Any pointers would be very helpful!
This feels a little janky but one can evidently freehand a circle in SVG then pass that drawn element as a marker to the map:
var icon = {
path: 'M-20,0a20,20 0 1,0 40,0a20,20 0 1,0 -40,0',
fillColor: '#FF0000',
fillOpacity: .6,
anchor: new google.maps.Point(0,0),
strokeWeight: 0,
scale: 1
}
var marker = new google.maps.Marker({
position: {lat: 55, lng: 0},
map: map,
draggable: false,
icon: icon
});
via How to use SVG markers in Google Maps API v3
Very similar to the answer posted by duhaime, I have used this method before to get a circle by using
google.maps.SymbolPath.CIRCLE
in the path property (SymbolPath is just an enum for a couple of built-in shapes, CIRCLE equates to '0').
Not entirely sure of the rationale (assume the original size is effectively 2px wide), but you can use 'scale' like the radius, so a scale of 20 will give a diameter of 40px. If you add strokeWeight, this will add to the overall diameter by the stroke width. I think the formula for overall diameter might be (scale + round(0.5 * stoke)) * 2, because if you use an odd number for the stroke it ends up being the even number above.
In the example below, you will end up with circle diameter of 46px (even though the stroke is only 5)
var icon = {
path: google.maps.SymbolPath.CIRCLE,
fillColor: "#da291c",
fillOpacity: 0.5,
strokeColor: "blue",
strokeOpacity: 1,
strokeWeight: 5,
scale: 20
}
var marker = new google.maps.Marker({
position: {lat: 57, lng: -2},
map: map,
icon: icon
});

Add my own icon for google map instead of the default google map marker

When I add new event then it shows the location of that event on the google map and it is showing the google default marker but I have to change the default icon with mine own icon.
I want to add my own icon on the google map instead of the default google marker.how is this possible?
Very simply. You can specify any image URL for the marker's icon property, e.g. here I'm using an alternative one of Google's, but that could just as easily be a path to an image file on your own server:
var marker = new google.maps.Marker({
position: map.getCenter(),
icon: "http://maps.google.com/mapfiles/ms/micons/blue.png",
map: map
});
This is very do able. You can use the Google Api to accomplish this. The maps api is not the easiest thing to use. I find with Google setting up the Api is the hard part. Once you get connected usually they document the code pretty well.
You can do it here,
This Link
// This example uses SVG path notation to add a vector-based symbol
// as the icon for a marker. The resulting icon is a star-shaped symbol
// with a pale yellow fill and a thick yellow border.
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: {lat: -25.363882, lng: 131.044922}
});
var goldStar = {
path: 'M 125,5 155,90 245,90 175,145 200,230 125,180 50,230 75,145 5,90 95,90 z',
fillColor: 'yellow',
fillOpacity: 0.8,
scale: 1,
strokeColor: 'gold',
strokeWeight: 14
};
var marker = new google.maps.Marker({
position: map.getCenter(),
icon: goldStar,
map: map
});
}

Draw radius in meters using Maps API v3 Data layer based on GeoJSON

I am using the Maps API v3 and added a GeoJSON file to create a circle (based on google.maps.Symbol objects) around each entry in the GeoJSON-file -- which works quite fine by using the setStyle-functionality:
map.data.addGeoJson('url_to_GeoJSON');
..
map.data.setStyle(function(feature) {
return /** #type {google.maps.Data.StyleOptions} */({
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 5,
fillColor: '#f00',
fillOpacity: 0.5,
strokeWeight: 0
}
});
});
Now I would need to draw a circle with a static radius in meters around each point, like it is provided by the regular google.maps.CircleOptions with its 'radius'.
Is there any possibility to use the very comfortable data layer 'addGeoJson'- and 'setStyle'-features in combination with a geographically correct radius in meters around each point?
I would be very happy to avoid setting up each marker manually "the old way" by iterating through the whole GeoJSON-file with
new google.maps.Circle({radius: 20000});
Any help is greatly appreciated! Thanks in advance!
After adding the code of Dr. Molle, there seems to be an issue while using multiple google.maps.Data-Objects, that should be shown/hide by checking/unchecking a checkbox within the website. This is my actual code, which already shows the data layer with drawn circles, but does not hide the circles of the specific data layer when unchecking a checkbox:
var map;
var dataset1 = new google.maps.Data();
var dataset2 = new google.maps.Data();
var dataset3 = new google.maps.Data();
function initialize() {
// Create a new map.
map = new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 6,
center: {lat: 50.678240, lng: 9.437256},
mapTypeId: google.maps.MapTypeId.TERRAIN
});
checkDataset();
}
function checkDataset() {
if (document.getElementById('chkDataset1').checked) {
// Define styles for dataPlug9 and apply to map-object.
dataset1.setStyle(function(feature) {
var geo = feature.getGeometry();
// Check for a point feature.
if(geo.getType().toLowerCase()==='point'){
//create a circle
feature.circle = new google.maps.Circle({
map: map,
center: geo.get(),
radius: 200000,
fillColor: '#FF0000',
fillOpacity: 0.05,
strokeColor: '#FF0000',
strokeOpacity: 0.4,
strokeWeight: 1
});
//trigger the dblclick-event of map.data on a dblclick on the circle
google.maps.event.addListener(feature.circle, 'dblclick',function(e){
e.stop();
google.maps.event.trigger(this.getMap().data,'dblclick', {feature:feature})
});
// Hide the marker-icon.
return {visible:false};
}});
// Remove feature on dblclick.
google.maps.event.addListener(dataset1,'dblclick',function(f){
this.remove(f.feature);
});
// Remove circle too when feature will be removed.
google.maps.event.addListener(dataset1,'removefeature',function(f){
try{f.feature.circle.setMap(null);}catch(e){}
});
dataset1.loadGeoJson('data/plug1.json');
dataset1.setMap(map);
} else {
dataset1.removefeature();
// This doesn't work either ..
dataset1.setMap(null);
}
}
I also added the above routine of function checkDataset() for the other 2 datasets (dataset2 and dataset3) and changed 'dataset1' to 'dataset2 / dataset3'.
You don't need to iterate "manually", setStyle already iterates over the features.
You may use it to execute additional code(e.g. create a google.maps.Circle):
map.data.setStyle(function(feature) {
var geo= feature.getGeometry();
//when it's a point
if(geo.getType().toLowerCase()==='point'){
//create a circle
feature.circle=new google.maps.Circle({map:map,
center: geo.get(),
radius: 20000,
fillColor: '#f00',
fillOpacity: 0.5,
strokeWeight: 0});
//and hide the marker when you want to
return {visible:false};
}});
Edit:
related to the comment:
The circles will be saved as a circle-property of the features(note: this property is not a property in the meaning of geoJSON, so it may not be accessed via getProperty).
You may add a listener for the removefeature-event and remove the circle there, so the circle will be removed when you remove the feature.
Sample code that will remove a feature(including the circle) on dblclick:
map.data.setStyle(function(feature) {
var geo= feature.getGeometry();
//when it's a point
if(geo.getType().toLowerCase()==='point'){
//create a circle
feature.circle=new google.maps.Circle({map:map,
center:geo.get(),
radius:200000,
fillColor: '#f00',
fillOpacity: 0.5,
strokeWeight: 0});
//trigger the dblclick-event of map.data on a dblclick on the circle
google.maps.event.addListener(feature.circle, 'dblclick',function(e){
e.stop();
google.maps.event.trigger(this.getMap().data,'dblclick',{feature:feature})
});
//and hide the marker
return {visible:false};
}});
//remove the feature on dblclick
google.maps.event.addListener(map.data,'dblclick',function(f){
this.remove(f.feature);
});
//remove the circle too when the feature will be removed
google.maps.event.addListener(map.data,'removefeature',function(f){
try{f.feature.circle.setMap(null);}catch(e){}
});

How to show google.maps.Circle like Marker in Street View mode?

I created some google.maps.Marker and did bind google.maps.Circle to it (see below):
But when I open street view, I see only Marker:
Does anybody know how to show Circle in street view mode?
Sounds like I can't do that.
Maybe someone knows how to show 2D/3D objects into Google-Street-View.
Any suggestion?
Thanks,
This is snippets of code:
var circle = {
strokeColor: "#006DFC",
strokeOpacity: 0.4,
strokeWeight: 2,
fillColor: "#006DFC",
fillOpacity: 0.15,
map: mapA,
center: selectedMarker.getPosition(),
radius: 50 // in meters
};
var cityCircle = new google.maps.Circle(circle);
cityCircle.bindTo('center', selectedMarker, 'position');
You could use a Symbol instead of a google.maps.Circle to draw the circle, it will be visible on the panorama.
Getting a 3D-effect would be more complicated, but it should be possible to modify the path of the symbol on the pov_changed-event of the panorama.

Categories