I have a function called draw() . I pass in the array value inside draw() to plot my desired marker on the map and its working well!!
function draw(j){
//for (let j =0; j< countvalue[0]; j++){
array[j].features.forEach(function (marker) {
// create a HTML element for each feature
var el = document.createElement('div');
el.className = 'marker';
var el = document.createElement('div');
el.className = 'marker';
el.style.backgroundImage =
'url(https:gin/images/pin.png)';
el.style.width = marker.properties.iconSize[0] + 'px';
el.style.height = marker.properties.iconSize[1] + 'px';
el.style.backgroundSize = '100%';
// make a marker for each feature and add it to the map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.setPopup(
new mapboxgl.Popup({ offset: 25 }) // add popups
.setHTML('</h3><p>' + marker.properties.description + '</p>' )
)
.addTo(map);
});
}
My Question:
How will i call the draw() function to remove the marker? Pardon me if this is not possible. Thanks a lot for your time
Create a list of markers that are being added to the map and pass a value to clear all existing markers when required.
let markersList = []
function draw(j, clearAll){
if(clearAll){
if(markersList){
for (var i = markersList.length - 1; i >= 0; i--) {
markersList[i].remove();
}
}
}
//for (let j =0; j< countvalue[0]; j++){
array[j].features.forEach(function (marker) {
// create a HTML element for each feature
var el = document.createElement('div');
el.className = 'marker';
var el = document.createElement('div');
el.className = 'marker';
el.style.backgroundImage =
'url(https:gin/images/pin.png)';
el.style.width = marker.properties.iconSize[0] + 'px';
el.style.height = marker.properties.iconSize[1] + 'px';
el.style.backgroundSize = '100%';
// make a marker for each feature and add it to the map
const newMarker = new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.setPopup(
new mapboxgl.Popup({ offset: 25 }) // add popups
.setHTML('</h3><p>' + marker.properties.description + '</p>' )
)
.addTo(map);
markersList.push(newMarker);
});
}
Question One: When closing an "infobox" how can I make it maps zoom back out?
Question Two: How would I make it so that only one "infobox" is open at a time?
I have tried adding:
google.maps.event.addListener(map, 'zoom_changed', function(){
if (! markers.length) { return; }
for (i in markers) {
markers[i].infoBox.close();
}
});
However the code dose not seem to work. Any thoughts?
function InfoBox(opts) {
google.maps.OverlayView.call(this);
this.latlng_ = opts.latlng;
this.map_ = opts.map;
this.content = opts.content;
this.offsetVertical_ = -195;
this.offsetHorizontal_ = 5;
this.height_ = 165;
this.width_ = 266;
var me = this;
this.boundsChangedListener_ =
google.maps.event.addListener(this.map_, "bounds_changed", function () {
return me.panMap.apply(me);
});
this.setMap(this.map_);
}
InfoBox.prototype = new google.maps.OverlayView();
InfoBox.prototype.remove = function () {
if (this.div_) {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
};
this.createElement();
if (!this.div_) return;
var pixPosition = this.getProjection().fromLatLngToDivPixel(this.latlng_);
if (!pixPosition) return;
this.div_.style.width = this.width_ + "px";
this.div_.style.left = (pixPosition.x + this.offsetHorizontal_) + "px";
this.div_.style.height = this.height_ + "px";
this.div_.style.top = (pixPosition.y + this.offsetVertical_) + "px";
this.div_.style.display = 'block';
};
InfoBox.prototype.createElement = function () {
var panes = this.getPanes();
var div = this.div_;
if (!div) {
div = this.div_ = document.createElement("div");
div.className = "infobox"
var contentDiv = document.createElement("div");
contentDiv.className = "content"
contentDiv.innerHTML = this.content;
var closeBox = document.createElement("div");
closeBox.className = "close";
closeBox.innerHTML = "x";
div.appendChild(closeBox);
function removeInfoBox(ib) {
return function () {
ib.setMap(null);
};
}
google.maps.event.addDomListener(closeBox, 'click', removeInfoBox(this));
div.appendChild(contentDiv);
div.style.display = 'none';
panes.floatPane.appendChild(div);
this.panMap();
} else if (div.parentNode != panes.floatPane) {
div.parentNode.removeChild(div);
panes.floatPane.appendChild(div);
} else {
.
}
}
InfoBox.prototype.panMap = function () {
var map = this.map_;
var bounds = map.getBounds();
if (!bounds) return;
var position = this.latlng_;
var iwWidth = this.width_;
var iwHeight = this.height_;
var iwOffsetX = this.offsetHorizontal_;
var iwOffsetY = this.offsetVertical_;
var padX = 40;
var padY = 40;
var mapDiv = map.getDiv();
var mapWidth = mapDiv.offsetWidth;
var mapHeight = mapDiv.offsetHeight;
var boundsSpan = bounds.toSpan();
var longSpan = boundsSpan.lng();
var latSpan = boundsSpan.lat();
var degPixelX = longSpan / mapWidth;
var degPixelY = latSpan / mapHeight;
var mapWestLng = bounds.getSouthWest().lng();
var mapEastLng = bounds.getNorthEast().lng();
var mapNorthLat = bounds.getNorthEast().lat();
var mapSouthLat = bounds.getSouthWest().lat();
var iwWestLng = position.lng() + (iwOffsetX - padX) * degPixelX;
var iwEastLng = position.lng() + (iwOffsetX + iwWidth + padX) * degPixelX;
var iwNorthLat = position.lat() - (iwOffsetY - padY) * degPixelY;
var iwSouthLat = position.lat() - (iwOffsetY + iwHeight + padY) * degPixelY;
var shiftLng =
(iwWestLng < mapWestLng ? mapWestLng - iwWestLng : 0) +
(iwEastLng > mapEastLng ? mapEastLng - iwEastLng : 0);
var shiftLat =
(iwNorthLat > mapNorthLat ? mapNorthLat - iwNorthLat : 0) +
(iwSouthLat < mapSouthLat ? mapSouthLat - iwSouthLat : 0);
var center = map.getCenter();
var centerX = center.lng() - shiftLng;
var centerY = center.lat() - shiftLat;
map.setCenter(new google.maps.LatLng(centerY, centerX));
google.maps.event.removeListener(this.boundsChangedListener_);
this.boundsChangedListener_ = null;
};
function initialize() {
var markers = [];
var myOptions = {
zoom: 3,
center: new google.maps.LatLng(-5.646, 20.0611),
mapTypeId: google.maps.MapTypeId.ROADMAP,
sensor: 'true'
}
var map = new google.maps.Map(document.getElementById("canvas-map"), myOptions);
var data = [
{
'id':1,
'content':'Hello my name is marker, I\'m from Google',
'position': {
'lat':-33,
'lng':150
}
},
{
'id':2,
'content':'I am the content of this infobox. Wow, what a text.<br><br>The good thing is: Tags are also possible',
'position': {
'lat':-34,
'lng':150
}
},
]
for (var i = 0; i < data.length; i++) {
var current = data[i];
var marker = new google.maps.Marker({
position: new google.maps.LatLng(current.position.lat, current.position.lng),
map: map,
content: current.content
});
markers.push(marker);
marker.addListener('click', function() {
map.setZoom(8);
map.setCenter(marker.getPosition());
});
google.maps.event.addListener(markers[i], "click", function (e) {
map.zoomOut();
map.setCenter(this.getPosition());
var infoBox = new InfoBox({
latlng: this.getPosition(),
map: map,
content: this.content
});
});
}
}
Answer to question 1
If you want the map to zoom out when the user closes an info window, you can listen for the closeclick event on the info window object.
infoWindow.addListener('closeclick', function() {
map.setZoom(3); // Set the desired zoom level
});
I couldn't get your code to work so I created a basic example of the above functionality in action. Clicking on the marker opens an info window and zooms the map in. Closing the info window, zooms the map back out.
Answer to question 2
Check out geocodezip's comments to your question. He provides links to previous questions that could help you figure out how to have only one info window open at a time.
I need to create a a text box between the route directions like in here :
For start i was thinking to add tooltip...but i can't place them in the middle of the route like in the picture, or set it's position to be relative.
You can create a custom overlay as described in the documentation:
https://developers.google.com/maps/documentation/javascript/customoverlays
The sample code snippet might be
function routeOverlay(point, map, content) {
this._point = point;
this._map = map;
this.div_ = null;
this._cnt = content;
this.setMap(map);
}
routeOverlay.prototype = new google.maps.OverlayView();
routeOverlay.prototype.onAdd = function () {
var div = document.createElement('div');
div.style.borderStyle = 'solid';
div.style.borderWidth = '1px';
div.style.backgroundColor = 'white';
div.style.position = 'absolute';
div.style.padding = '4px';
div.style.zIndex = 1000;
div.innerHTML = this._cnt;
this.div_ = div;
// Add the element to the "overlayLayer" pane.
var panes = this.getPanes();
panes.overlayLayer.appendChild(div);
}
routeOverlay.prototype.draw = function () {
var overlayProjection = this.getProjection();
var p = overlayProjection.fromLatLngToDivPixel(this._point);
this.div_.style.left = p.x + 'px';
this.div_.style.top = p.y + 'px';
}
routeOverlay.prototype.onRemove = function () {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
routeOverlay.prototype.hide = function() {
if (this.div_) {
// The visibility property must be a string enclosed in quotes.
this.div_.style.visibility = 'hidden';
}
};
routeOverlay.prototype.show = function() {
if (this.div_) {
this.div_.style.visibility = 'visible';
}
};
Later in you code you should create this overlay. For example:
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var ind = (response.routes[0].overview_path.length - response.routes[0].overview_path.length % 2) / 2;
var rover = new routeOverlay(response.routes[0].overview_path[ind], map, 'Add your HTML here');
rover.show();
} else {
window.alert('Directions request failed due to ' + status);
}
I have modified your jsfiddle: http://jsfiddle.net/h54tmmcb/14/
You should create corresponding styles and corresponding content for the overlay.
Hope this helps!
I've created a fiddle example here where anyone can check my problem.
That problem is that when two overlays (built with a svg element and a path inside it) are too close, i.e, side by side, and both shapes have irregular limits, then the last shape rendered is overlapping the first one.
Visually you can't appreciate it, but if you add, e.g., a click event to the path dom elements to show their names, the last rendered shape is clickable in al its region, but the first one is not entirely clickable. The marked region in the following picture is not clickable for the bottom picture but it should be:
There is a css rule to change the cursor when it is over a path. You can also see that in the specified region the cursor does not change.
How can I avoid this overlapping? What I want is to do the bottom shape entirely clickable.
Finally I tried to find another solution like getting all elements under the mouse position and determine which path was, at that moment, under the pointer, and execute the action on it. I was looking for a way to implement this solution and I found this post: solution!!, where there is just what I needed.
I think it is the best and more elegant way (at least better than I thought before) to solve my issue. Basically it adds the css rule pointer-events: none; to the top svg element which is blocking other path elements. And to the pathobjects I have added pointer-events: all;. So the css styles should look like this:
svg.overlay {
pointer-events: none;
}
svg.overlay > path {
pointer-events: all;
}
The entire solution (you can see it also in a jsfiddler example):
function initialize() {
var myLatLng = new google.maps.LatLng(42, -111.02783203125);
var mapOptions = {
zoom: 6,
center: myLatLng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
//polygon coords for Utah
var path = [
new google.maps.LatLng(41.983994270935625, -111.02783203125),
new google.maps.LatLng(42.00032514831621, -114.01611328125),
new google.maps.LatLng(36.96744946416931, -114.01611328125),
new google.maps.LatLng(37.00255267215955, -109.0283203125),
new google.maps.LatLng(40.97989806962013, -109.0283203125),
new google.maps.LatLng(41.0130657870063, -111.02783203125)
];
// custom overlay created
var overlay = new BW.PolyLineFill(path, map, 'red', '#000', 'original');
// polygon coords for conflict shape
var pathConflict = [
new google.maps.LatLng(42.00032514831621, -114.01611328125),
new google.maps.LatLng(41.983994270935625, -111.02783203125),
new google.maps.LatLng(41.0130657870063, -111.02783203125),
new google.maps.LatLng(40.97989806962013, -109.0283203125),
new google.maps.LatLng(47.00255267215955, -109.0283203125),
new google.maps.LatLng(46.96744946416931, -114.01611328125)
];
var overlayConflict = new BW.PolyLineFill(pathConflict, map, 'white', '#000', 'conflict');
}
///Start custom poly fill code
PolyLineFill.prototype = new google.maps.OverlayView();
function PolyLineFill(poly, map, fill, stroke, name) {
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < poly.length; i++) {
bounds.extend(poly[i]);
}
//initialize all properties.
this.bounds_ = bounds;
this.map_ = map;
this.$dom = null;
this.poly_ = poly;
this.polysvg_ = null;
this.fill_ = fill;
this.stroke_ = stroke;
this.name_ = name;
// Explicitly call setMap on this overlay
this.setMap(map);
}
PolyLineFill.prototype.onAdd = function() {
//createthe svg element
var svgns = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(svgns, "svg");
svg.setAttributeNS(null, "preserveAspectRatio", "xMidYMid meet");
var def = document.createElementNS(svgns, "defs");
//create the pattern fill
var pattern = document.createElementNS(svgns, "pattern");
pattern.setAttributeNS(null, "id", "lineFill-" + this.name_);
pattern.setAttributeNS(null, "patternUnits", "userSpaceOnUse");
pattern.setAttributeNS(null, "patternTransform", "rotate(-45)");
pattern.setAttributeNS(null, "height", "7");
pattern.setAttributeNS(null, "width", "7");
def.appendChild(pattern);
var rect = document.createElementNS(svgns, "rect");
rect.setAttributeNS(null, "id", "rectFill");
rect.setAttributeNS(null, "fill", this.fill_ || "red");
rect.setAttributeNS(null, "fill-opacity", "0.3");
rect.setAttributeNS(null, "stroke", this.stroke_ || "#000");
rect.setAttributeNS(null, "stroke-dasharray", "7,7");
rect.setAttributeNS(null, "height", "7");
rect.setAttributeNS(null, "width", "7");
pattern.appendChild(rect);
svg.appendChild(def);
//add path to the div
var path = document.createElementNS(svgns, 'path');
path.setAttributeNS(null, 'fill', 'url(#lineFill-' + this.name_ + ')');
path.setAttributeNS(null, 'stroke', '#000');
path.setAttributeNS(null, 'stroke-width', '1');
path.setAttributeNS(null, 'pointer-events', 'all');
this.path_ = path;
svg.appendChild(this.path_);
svg.style.borderStyle = 'none';
svg.style.borderWidth = '0px';
svg.style.position = 'absolute';
svg.style.pointerEvents = 'none';
svg.setAttribute('class', 'polygon');
this.$dom = svg;
// We add an overlay to a map via one of the map's panes.
// We'll add this overlay to the overlayLayer pane.
var panes = this.getPanes();
panes.overlayMouseTarget.appendChild(this.$dom);
var dragging = false;
google.maps.event.addDomListener(this.path_, 'mousedown', function(evt) {
dragging = false;
});
google.maps.event.addDomListener(this.path_, 'mousemove', function(evt) {
dragging = true;
});
var _self = this;
// onclick listener
google.maps.event.addDomListener(this.path_, 'click', function(evt) {
if (dragging) {
return false;
}
alert('clicked on ' + _self.name_);
});
}
PolyLineFill.prototype.AdjustPoints = function() {
//adjust the polygon points based on the projection.
var proj = this.getProjection();
var sw = proj.fromLatLngToDivPixel(this.bounds_.getSouthWest());
var ne = proj.fromLatLngToDivPixel(this.bounds_.getNorthEast());
var points = "";
for (var i = 0; i < this.poly_.length; i++) {
var point = proj.fromLatLngToDivPixel(this.poly_[i]);
if (i == 0) {
points += (point.x - sw.x) + ", " + (point.y - ne.y);
} else {
points += " " + (point.x - sw.x) + ", " + (point.y - ne.y);
}
}
return points;
}
PolyLineFill.prototype.draw = function() {
// Size and position the overlay. We use a southwest and northeast
// position of the overlay to peg it to the correct position and size.
// We need to retrieve the projection from this overlay to do this.
var overlayProjection = this.getProjection();
// Retrieve the southwest and northeast coordinates of this overlay
// in latlngs and convert them to pixels coordinates.
// We'll use these coordinates to resize the DIV.
var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
// Resize the image's DIV to fit the indicated dimensions.
var div = this.$dom;
div.style.left = sw.x + 'px';
div.style.top = ne.y + 'px';
div.style.width = (ne.x - sw.x) + 'px';
div.style.height = (sw.y - ne.y) + 'px';
this.path_.setAttributeNS(null, "d", 'M' + this.AdjustPoints() + 'z');
}
PolyLineFill.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
window.BW = {};
window.BW.PolyLineFill = PolyLineFill;
///end poly fill code
google.maps.event.addDomListener(window, 'load', initialize);
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
.polygon: {
pointer-events: none;
}
.polygon > path {
cursor: pointer;
pointer-events: all;
}
.polygon > path:active {
cursor: -webkit-grabbing;
}
#map-canvas,
#map_canvas {
height: 100%;
}
#media print {
html,
body {
height: auto;
}
#map_canvas {
height: 650px;
}
}
<script src="http://maps.google.com/maps/api/js?sensor=false&.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="map-canvas"></div>
I am having issues with a second CustomMarker. I can see the paths on the map, but the anchor points are not showing up with my custom image. What am I missing?
My code for the second Custom Marker is:
/* ***** Begin CustomMarker Toekomst ***** */
function CustomMarkerToekomst(latlng, map, marker_id, hovercard) {
this.latlng_ = latlng;
this.marker_id = marker_id;
this.hovercard_content = hovercard;
this.setMap(map);
}
CustomMarkerToekomst.prototype = new google.maps.OverlayView();
CustomMarkerToekomst.prototype.draw = function() {
var me = this;
var div = this.div_;
if (!div) {
div = this.div_ = document.createElement('DIV');
div.id=me.marker_id;
var panes = this.getPanes();
panes.overlayImage.appendChild(div);
}
var point = this.getProjection().fromLatLngToDivPixel(this.latlng_);
if (point) {
div.className = 'map-marker-toekomst show-hovercard';
div.style.left = (point.x-6) + 'px';
div.style.top = (point.y-23) + 'px';
$(div).attr('data-hovercard-content', me.hovercard_content);
}
};
CustomMarkerToekomst.prototype.remove = function() {
if (this.div_) {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
};
CustomMarkerToekomst.prototype.getPosition = function() {
return this.latlng_;
};
/* ***** Eind CustomMarker Toekomst ***** */
My css for the div 'map-marker-toekomst' is; I have verified that the background does exist.:
.map-marker-toekomst {
position: absolute;
width: 21px;
height: 25px;
background: url(removed/img/mapmarker_blue.png) no-repeat;
cursor: pointer;
}
My results are populated from a database query from php into the javascript as:
echo " pos = new google.maps.LatLng(" . $row['latitudedecimal'] . "," .
$row['longitudedecimal'] . ");\n";
echo " overlay = new CustomMarkerToekomst(pos, map, \"" . $row['marker']
. "\", '" . $row['luchthavennaam'] . "');\n";
echo " overlay.setMap(map);\n";
echo " bounds.extend(pos);\n";
echo " \n";
I have a similiar CustomMarker just above this script which works fine, it's this second one that is the problem.