How do I programatically in javascript define which feature is on top of another when they overlap
In This picture I clicked on the smaller geographic feature, however the information for the outer geographic feature came up. I would like to programmatically put the smaller features on top.
Here is the my code to populate the map in javascript-
map.data.loadGeoJson('<path to my geojson definition>');
map.data.setStyle(function (feature) {
let color = 'gray';
console.log(feature);
if (feature.getProperty('isColorful')) {
color = 'blue';
}
return ({
fillColor: color,
strokeColor: color,
strokeWeight: 1
});
});
map.data.addListener('click', function (event) {
let name = event.feature.getProperty('name');
let contentString = '<h4>Polygon Info</h4><br>';
contentString += '<p>';
if (name) {
contentString += '<b>Name:</b>' + name + '<br>';
}
contentString += '</p>';
let infowindow = new google.maps.InfoWindow({
content: contentString,
position: event.latLng
});
infowindow.open(map);
event.feature.setProperty('isColorful', true);
});
Note I reversed the order of the features in the geojson file itself and that did not change the behavior.
You can use the z-index property of feature.Assuming you can disinguish one feature entry from another you can adjust z-index depending on your need in setStyle() function, just work out appropriate values
return ({
fillColor: color,
strokeColor: color,
strokeWeight: 1,
zIndex: zIndex
});
Or update z-index when mouse enters
map.data.addListener('mouseover', function(e) {
map.data.overrideStyle(e.feature, {
//strokeColor: '#1e3a2a',
//strokeWeight: 2, /* can also change these like hover effect */
zIndex: 6
});
});
map.data.addListener('mouseout', function(e) {
map.data.revertStyle();
});
Related
// Create the map object with center, zoom level and default layer.
let map = L.map('mapid', {
center: [40.7, -94.5],
zoom: 3,
layers: [streets]
});
// Create a base layer that holds all three maps.
let baseMaps = {
"Streets": streets,
"Satellite": satelliteStreets,
"Night Navigation": nightNav
};
// 1. Add a 2nd layer group for the tectonic plate data.
let allEarthquakes = new L.LayerGroup();
let allTectonics = new L.LayerGroup();
let majorEQ = new L.LayerGroup();
// 2. Add a reference to the tectonic plates group to the overlays object.
let overlays = {
"Earthquakes": allEarthquakes,
"Tectonic Plates": allTectonics,
"Major Earthquakes": majorEQ
};
// Then we add a control to the map that will allow the user to change which
// layers are visible.
L.control.layers(baseMaps, overlays).addTo(map);
let tectonicData = "https://raw.githubusercontent.com/fraxen/tectonicplates/master/GeoJSON/PB2002_boundaries.json";
let tstyle = map.on('baselayerchange', function(feature) {
console.log('base layer has been changed');
return {
fillColor: tecStyle(L.overlays),
fillOpacity: 0.8,
weight: 0.5
};
});
function tecStyle(feature) {
if (feature === baseMaps["Streets"]) { return "purple";
}
if (feature === baseMaps["Satellite"]) {
return "red";
}
if (feature === baseMaps["Night Navigation"]) {
return "red";
}
};
// 3. Use d3.json to make a call to get our Tectonic Plate geoJSON data.
d3.json(tectonicData).then(function(data) {
// Create GeoJSON layer with the retrieved data.
L.geoJSON(data, {
style: tstyle,
onEachFeature: function(feature, layer){
layer.bindPopup("<h3><b> Plate Boundary Name: " + feature.properties.Name + "</h3></b>");
}
}).addTo(allTectonics);
allTectonics.addTo(map);
});
});
I want the tectonic fault line color to change colors depending on whether the user is on the 'Streets', 'Satellite', or 'Night Navigation' layers. For example, I want the fault line color to be 'purple' when on 'Streets', 'red' when on 'Satellite', and 'blue' when on 'Night Navigation' base layer maps. I was hoping I could get it to work using map.on and the event listener is working, because it comes up in my DevTools console, but the color of the fault line remains the default color of 'blue' no matter what layer I am on. What am I doing wrong here? or is there an easier way to do this?
update "style: tstyle" in step 3 to "style: tecStyle" and you should be good to go!
I'm trying to make a chloropleth map of the 50 states.
Each state has a potential info window.
The catch is when a user clicks a state, an infowindow opens. The challenge is to close it when another state is clicked. The problem is somewhere in the code below, I can't even get the first infowindow open.
The error I get is: Uncaught ReferenceError: can't access lexical declaration 'infowindow' before initialization
"use strict";
let map;
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
zoom: 6,
center: {
lat: 28,
lng: -85
}
}); // Load GeoJSON.
map.data.loadGeoJson(
//"https://storage.googleapis.com/mapsdevsite/json/google.json"
"maps/US_ALL_STATES.geojson"
); // Color each letter gray. Change the color when the isColorful property
// is set to true.
map.data.setStyle(feature => {
let color = "blue";
if (feature.getProperty("isColorful")) {
color = feature.getProperty("color");
}
return (
/** #type {!google.maps.Data.StyleOptions} */
({
fillColor: color,
strokeColor: color,
strokeWeight: 2
})
);
}); // When the user clicks, set 'isColorful', changing the color of the letters.
map.data.addListener("click", event => {
if (infowindow) infowindow.close();
var content = "State: " + event.feature.getProperty("NAME") + "\r"
content = content + "Click <a href='county.html?state="+event.feature.getProperty("STATEFP")+"'>here</a> to Zoom in"
const infowindow = new google.maps.infowindow({
content: content
});
event.feature.setProperty("fillColor", "pink");
infowindow.setPosition(event.latLng);
infowindow.open(map);
console.log(event.feature);
}); // When the user hovers, tempt them to click by outlining the letters.
// Call revertStyle() to remove all overrides. This will use the style rules
// defined in the function passed to setStyle()
map.data.addListener("mouseover", event => {
map.data.revertStyle();
map.data.overrideStyle(event.feature, {
strokeWeight: 8,
fillColor: "green",
strokeColor: "pink"
});
});
map.data.addListener("mouseout", event => {
map.data.revertStyle();
});
}
how do update this to make it a "one infowindow at a time" scenerio? Thank you.
Create the variable in the persistent outside scope instead:
let infowindow;
map.data.addListener("click", event => {
if (infowindow) infowindow.close();
var content = "State: " + event.feature.getProperty("NAME") + "\r"
content = content + "Click <a href='county.html?state="+event.feature.getProperty("STATEFP")+"'>here</a> to Zoom in"
infowindow = new google.maps.infowindow({
content: content
});
// etc
I have 2 options in my google map. -->one is polygon and another one is draw lines.Now am trying to perform undo functionality in my page.When I click undo button recently added line should be removed(that may be in either polygon option or draw line option).Is it possible?
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
var drawingManager = new google.maps.drawing.DrawingManager({
drawingMode: google.maps.drawing.OverlayType.MARKER,
drawingControl: true,
drawingControlOptions: {
position: google.maps.ControlPosition.TOP_CENTER,
drawingModes: ['polygon', 'polyline']
},
circleOptions: {
strokeColor: '#00DB00',
fillColor: 'green',
fillOpacity: 0.05,
strokeWeight: 5,
clickable: false,
editable: true,
zIndex: 1
}
});
drawingManager.setMap(map);
google.maps.event.addListener(drawingManager, 'overlaycomplete', function (event)
{
if (event.type === 'marker') console.log('Lat: ' + event.overlay.position.lat() + ', Long: ' + event.overlay.position.lng())
else displayPolyLatLng(event.overlay.getPath().b);
});
function displayPolyLatLng(pointArray)
{debugger
var result=" ";
for (var i = 0; i < pointArray.length; i++)
{
result +='<b>Lat: ' + pointArray[i].lat() + ', Long: ' + pointArray[i].lng()+ '</b><br/>';
}
$('#info').html(result);
/*var lastEl = pointArray[pointArray.length-1];
alert(lastEl)
if(lastEl.length>1){
undo_redo.push(lastEl.pop());
} */
}
}
function removeLine(){
polylines.remove(polylines.size() - 1)
}
fiddle: https://jsfiddle.net/rc5p32nt/
Basically what #geocodezip said...
Keep a reference to the shapes added:
var overlays = [];
You can then push the latest overlay object event.overlay in the overlaycomplete event:
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(event) {
overlays.push(event.overlay); // store reference to added overlay
});
Finally, on click of the Undo button hide the overlay via setMap(null):
// undo last overlay action
function removeLine() {
var lastOverlay = overlays.pop();
if (lastOverlay) lastOverlay.setMap(null);
}
Note that this doesn't remove the object, just hides it from showing on the map. If you want to remove the overlay completely you should then also set it to null.
You can show them again - if you wanted to add a Redo button (in this case you wouldn't pop them as I have done). See https://developers.google.com/maps/documentation/javascript/examples/polyline-remove for an example of how to implement this.
Updated demo: https://jsfiddle.net/3fe6nfdr/
My above answer explains how to remove the most recent overlay entirely.
However to remove the most recently drawn line segment of a polyline you can store a reference to the shape object in the overlaycomplete event object:
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(event) {
overlays.push(event); // store reference to added overlay
});
Then using that determine if the object added was a polyline, grab the path array with overlay.getpath(), and finally call its pop() function to remove the last line segment:
// undo the last line segment of a polyline
function removeLineSegment() {
var lastOverlay = overlays.length > 0 ? overlays[overlays.length - 1] : null;
if (lastOverlay && lastOverlay.type === "polyline") {
var path = lastOverlay.overlay.getPath();
path.pop(); // remove last line segment
}
}
Demo: https://jsfiddle.net/q9xxt4kt/
I was able to make a circle object as an overlay on my google map v3. I set its editable property to true. The next thing I wanted to do was get the coordinates of the center if the user moves the circle. For this I would need some kind of method that's triggered in response to the event. I thought I had this all set up in the initialize function as seen below. However, I'm not getting any alert boxes. So I'm assuming this functions in response to the events are not getting triggered.
function initialize() {
cityCenterLatLng = new google.maps.LatLng(cLat, cLong);
options = {
center : cityCenterLatLng,
zoom : 15,
mapTypeId : google.maps.MapTypeId.ROADMAP,
disableDefaultUI : false,
mapTypeControl : true,
streetViewControl : true,
mapTypeControlOptions : {
style : google.maps.MapTypeControlStyle.DEFAULT,
position : google.maps.ControlPosition.TOP_LEFT
}
};
map = new google.maps.Map(document.getElementById("map_canvas"), options);
$("#map_canvas").css("border", "3px solid black");
infoWindow = new google.maps.InfoWindow({});
var c = {
strokeColor: "#ff0000",
strokeOpacity: 0.8,
strokeWeight: 3,
fillColor: "#b0c4de",
fillOpacity: 0.50,
map: map,
center: cityCenterLatLng,
radius: 1000,
editable:true
};
circle = new google.maps.Circle(c);
google.maps.event.addListener(circle, 'distance_changed', function()
{
alert('Circle moved');
//displayInfo(circle);
});
google.maps.event.addListener(circle, 'position_changed', function()
{
alert('distance changed');
//displayInfo(circle);
});
}
function displayInfo(widget)
{
var info = document.getElementById('info');
info.innerHTML = 'Circle Center:' + circle.get('position') + ',Circle distance: ' +
circle.get('distance');
}
Change distance_changed to center_changed for dragged movement, and use radius_changed to detect resizing. Other events are in the docs.
https://developers.google.com/maps/documentation/javascript/reference#Circle
scroll down to 'Events'.
For the new values, inside the listener functions, write alert(circle.getCenter()); or circle.getRadius()
I have a click event that grabs coordinates remotely, lays them on a map, and connects them with a polyline. I have other click events that do the same, but want to keep them as a separate layer so that the user can toggle to view each of the clicks they have done. Does anyone know of a good way to group them separately to do this or another approach?
Here is my code so far:
drawPoints: function (points, color) {
$.each(points, function (key, val) {
var location = new google.maps.LatLng(val.Latitude, val.Longitude);
MyTest.addMarker(location, val.Name, val.Description);
MyTest.coordinates.push(location);
});
this.path = new google.maps.Polyline({
path: MyATest.coordinates,
strokeColor: color,
strokeOpacity: 1.0,
strokeWeight: 2
});
this.path.setMap(this.googlemap);
},
addMarker: function (location, title, html) {
var marker = new google.maps.Marker({
position: location,
map: this.googlemap,
title: title,
html: html
});
this.markers.push(marker);
},
// Removes the overlays from the map, but keeps them in the array
clearOverlays: function () {
if (this.markers) {
for (i in this.markers) {
this.markers[i].setMap(null);
}
}
},
// Shows any overlays currently in the array
showOverlays: function () {
if (this.markers) {
for (i in this.markers) {
this.markers[i].setMap(map);
}
}
},
You've got them kept in this.markers - which is a great start. Now all you need to do is define a click listener function for each marker:
for(i in this.markers) {
google.maps.addListener(this.markers[i], function() {
this.markers[i].clicked = true; });
}
Then, whenever a user wants to toggle only the markers they've clicked on, loop through your markers (like you are doing) and setMap to null on all where this.markers[i].clicked === undefined.