Disable drag on Google Map Javascript custom controls - javascript

I want to create a Google map custom control using the Javascript API.
The control is an 'add waypoint' button. The user is supposed to be able to drag from the control, causing a marker to appear at the mouse pointer, and drop this marker on to the map.
The intended behavior is nearly identical to the existing pegman feature.
The problem is that the Google map controls seem to interact with drag, even when the drag has been explicitly disabled, as seen below.
In the example code, The first time I click and drag, it works as intended. The second time I click and drag, I get a 'no drag' icon and the control spawns a ghost of itself. The third time I drag, it's working again. On the next drag, there's a 'no drag'. The sequence continues, with odd drags working, and even drags failing.
Here's what I believe to be the relevant section:
<div id="map"></div>
<script>
var spawning = false;
var inProgress = false;
var waypointSpawner;
var targetLat;
var targetLng;
function initMap()
{
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 0.0000, lng: 0.0000},
zoom: 2
});
// Create the DIV to hold the control and call the CenterControl()
// constructor passing in this DIV.
var centerControlDiv = document.createElement('div');
var centerControl = new CenterControl(centerControlDiv, map);
centerControlDiv.index = 1;
map.controls[google.maps.ControlPosition.LEFT_TOP].push(centerControlDiv);
}
function CenterControl(controlDiv, map) {
// Set CSS for the control border.
var controlUI = document.createElement('div');
controlUI.style.backgroundColor = '#fff';
controlUI.style.border = '2px solid #fff';
controlUI.style.borderRadius = '3px';
controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)';
controlUI.style.cursor = 'pointer';
controlUI.style.marginBottom = '22px';
controlUI.style.width = '40px';
controlUI.style.height = '50px';
controlUI.style.opacity = "0.7";
controlUI.style.backgroundImage = "url('create_waypoint_icon.png')";
controlUI.title = 'Add Waypoint';
controlUI.draggable = "false";
//controlDiv.draggable = "false";
controlDiv.appendChild(controlUI);
}
I was unable to get the above code to work in jsfiddle, but there are a couple of other examples that illustrate the problem.
http://jsfiddle.net/maunovaha/jptLfhc8/
Attempting to drag and drop the blue and green squares in the top left of the map results in some unintended behavior. Depending on your zoom level, and somewhat randomly, you will get a variety of different outcomes -
1 - Nothing happens. The pointer remains as a finger and moves normally.
2 - The finger pointer changes to a 'no drag' but otherwise moves normally.
3 - The finger pointer changes to a 'no drag' and a blue-and green semi-transparent 'to-paste' widget.
4 - The finger pointer changes to a 'no drag', and either the blue or green half of the widget (depending on your selection) goes with it.
This behavior is also somewhat evident in the Google control example code:
https://developers.google.com/maps/documentation/javascript/examples/control-custom
In this instance, when the text is highlighted, you get the 'no drag' icon. The parent control doesn't appear to be selectable, which is the desired goal.
I believe that the custom control is acquiring some kind of 'focus' status, and that clicking it again clears the focus. This behavior is evident in the green/blue example. Perhaps the control is being 'highlighted' somehow?
I attempted to implement the solutions given in How can I make a div unselectable? to no effect. I also tried a variety of ways to make the div undraggable in the code, but this did not appear to have any impact.
I also tried simulating mouse clicks on other parts of the program, and double clicking on the control, to change the focus/highlight status, but it appears the API doesn't actually carry out the clicks, only generates events from them. I couldn't find a way to cause a 'real' click. Manually clicking, then clicking and dragging worked.
I also tried making the control draggable but it still has the 'no drag' mouse pointer even when dragging it about.

If I understand correctly what you are saying, you want to be able to drag a marker onto the map, from outside, and place a point on that position.
I have the following solution which does that.
First we need to build the map:
function buildMap() {
var g = google.maps;
var mapOptions = {
center: new g.LatLng(52.052491, 9.84375),
zoom: 4,
mapTypeId: g.MapTypeId.ROADMAP,
streetViewControl: false,
panControl: false
};
map = new g.Map(document.getElementById("map"), mapOptions);
iw = new g.InfoWindow();
g.event.addListener(map, "click", function() {
if (iw) iw.close()
});
drag_area = document.getElementById("markers");
var b = drag_area.getElementsByTagName("div");
b[0].onmousedown = initDrag
dummy = new DummyOView()
}
We create a standard map and add features such as infoWindows to display the lat lng when the marker is added.
In addition we get where the marker is held and onMouseDown call a function (soon to come).
We use b[0] as we are getting the first set of <div> tags in the mark-up below, this is where the draggable icon is held:
<div id="markers">
<div id="m1" class="drag" style="left:0; background-image: url('https://maps.gstatic.com/mapfiles/ms/icons/ltblue-dot.png')">
</div>
</div>
The DummyOView is a map OverlayView which allows us to drag onto the map and grab the coords and position. More information on SO + credit to : More info
function DummyOView() {
this.setMap(map);
this.draw = function() {}
}
DummyOView.prototype = new google.maps.OverlayView();
The initDrag function is where most of the work is done, this is quite a lengthy function so I did comment the code, any questions on clarification just add a comment.
//function that allows us to drag the marker from the div to the map
function initDrag(e) {
//allows us to drag the marker and keep record of the clientX client Y coordinates
var j = function(e) {
var a = {};
if (!e) var e = window.event;
a.x = e.clientX;
a.y = e.clientY
return a
};
//function called whenever the mouse moves. this will keep track of the marker as we move around
var k = function(e) {
//check to ensure that the object is of class drag - otherwise we could drag everything
if (obj && obj.className == "drag") {
var i = j(e),
deltaX = i.x - l.x,
deltaY = i.y - l.y;
obj.style.left = (obj.x + deltaX) + "px";
obj.style.top = (obj.y + deltaY) + "px";
obj.onmouseup = function() {
//get the information to check to see if the dragObj is on the map on mouse up
var a = map.getDiv(),
mLeft = a.offsetLeft,
mTop = a.offsetTop,
mWidth = a.offsetWidth,
mHeight = a.offsetHeight;
var b = drag_area.offsetLeft,
areaTop = drag_area.offsetTop,
oWidth = obj.offsetWidth,
oHeight = obj.offsetHeight;
//check to see if obj is in bounds of map div X and Y
var x = obj.offsetLeft + b + oWidth / 2;
var y = obj.offsetTop + areaTop + oHeight / 2;
if (x > mLeft && x < (mLeft + mWidth) && y > mTop && y < (mTop + mHeight)) {
var c = 1;
var mapTemp = google.maps;
var point = new mapTemp.Point(x - mLeft - c, y - mTop + (oHeight / 2));
var proj = dummy.getProjection();
var latlng = proj.fromContainerPixelToLatLng(point);
var backImage = obj.style.backgroundImage.slice(4, -1).replace(/"/g, "");
createDraggedMarker(latlng, backImage);
fillMarker(backImage)
}
}
}
return false
};
//assign the event to a windows event
obj = e.target ? e.target : e.srcElement;
//if the object where the event took place is not called drag cancel the event
if (obj.className != "drag") {
if (e.cancelable) e.preventDefault();
obj = null;
return
} else {
z_index += 1;
obj.style.zIndex = z_index.toString();
obj.x = obj.offsetLeft;
obj.y = obj.offsetTop;
var l = j(e); //get the initial position of the marker relative to the client
document.onmousemove = k;
//if we lift the mouse up outside the map div set to null and leave where it is
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
if (obj) obj = null
}
}
return false
}
Summarised this function is called whenever the mouse has been clicked and dragged. It registed the starting position and the positions of the map div and checks when the onmouseup to see if the current mouse position is over the map div. If so we create a marker on the map and re-add the marker icon to the div to allow redragging.
//when the marker is dragged onto the map
//we call this function to create a marker on the map
function createDraggedMarker(position, iconImage) {
var mapLocal = google.maps;
var icon = {
url: iconImage,
size: new mapLocal.Size(32, 32),
anchor: new mapLocal.Point(15, 32)
};
var marker = new mapLocal.Marker({
position: position,
map: map,
clickable: true,
draggable: true,
crossOnDrag: false,
optimized: false,
icon: icon,
zIndex: highestOrder()
});
mapLocal.event.addListener(marker, "click", function() {
actual = marker;
var lat = actual.getPosition().lat();
var lng = actual.getPosition().lng();
var innerHtml = "<div class='infowindow'>" + lat.toFixed(6) + ", " + lng.toFixed(6) + "<\/div>";
iw.setContent(innerHtml);
iw.open(map, this)
});
mapLocal.event.addListener(marker, "dragstart", function() {
if (actual == marker) iw.close();
z_index += 1;
marker.setZIndex(highestOrder())
})
}
Re-adding the marker to the div
//Used to replace the marker
//once we have moved it from its div
function fillMarker(a) {
var div = document.createElement("div");
div.style.backgroundImage = "url(" + a + ")";
var padding;
if (obj.id == "m1") {
padding = "0px"
}
div.style.left = padding;
div.id = obj.id;
div.className = "drag";
div.onmousedown = initDrag;
drag_area.replaceChild(div, obj);
obj = null
}
JSfiddle you can use : https://jsfiddle.net/q3wjrpdw/7/
Any questions feel free to ask.

Related

Leaflet/JS: Adding polylines after pan/zoom

I am using a Leaflet map as an image viewer, and I'm trying to add a 'toggle crosshairs' function that will place a crosshair at the center of the image. I've got it working to a point where the crosshair appears correctly, and is 'coupled' to the image when doing pans/zooms, which is what I would expect to see.
The issue I'm having is that if I do the pan/zoom FIRST, and then toggle the crosshairs, the crosshairs appear in the center of the 'window', not in the center of the image.
The code I'm using for this is here:
toggleCrosshair(toggleVal) {
let map = this.map;
if (toggleVal == true) {
var north = map.getBounds().getNorth(); // these are in lat, lng
var south = map.getBounds().getSouth();
var east = map.getBounds().getEast();
var west = map.getBounds().getWest();
var center = map.getCenter();
var latlngHorizontalLeft = new L.latLng(center.lat, west);
var latlngHorizontalRight = new L.latLng(center.lat, east);
var horizLineList = [latlngHorizontalLeft, latlngHorizontalRight];
let horizLine = new L.Polyline(horizLineList, {
color: 'red',
weight: 2,
opacity: 0.5,
smoothFactor: 1
});
var latlngVerticalTop = new L.latLng(north, center.lng);
var latlngVerticalBottom = new L.latLng(south, center.lng);
var vertLineList = [latlngVerticalTop, latlngVerticalBottom];
let vertLine = new L.Polyline(vertLineList, {
color: '#A5CE3A',
weight: 2,
opacity: 0.5,
smoothFactor: 1
});
horizLine.addTo(map);
vertLine.addTo(map);
} else {
var i;
for (i in map._layers) {
if (map._layers[i].options.color == '#A5CE3A') {
try {
map.removeLayer(map._layers[i]);
} catch (e) {
console.log("problem with " + e + map._layers[i]);
}
} else if (map._layers[i].options.color == 'red') {
try {
map.removeLayer(map._layers[i]);
} catch (e) {
console.log("problem with " + e + map._layers[i]);
}
}
}
}
}
Does anyone have any hints on how to get the behavior I'm hoping for? Thanks!
map.getBounds() only gives you the geographical bounds visible in the current map view.
So in short, whatever you see on the screen.
Set it to the actual center lat lng coordinates.
https://leafletjs.com/reference-1.6.0.html#map-getbounds
Thanks for the responses. I ended up saving the original location/zoom value, moving the image to a known location/zoom value (using the SetView method), then adding the cross hairs, and then moving the image back to the original location/zoom values. I did have to set the animation to false in the SetView calls...the timing of the animation threw things off.
Probably not the most elegant solution, but its something I remember having to do something similar when using Canvas elements.

Re-size circle using paperJS

I am trying to re-size a circle using papeJS but since i used two onMouseDrag function it if conflicting. I am unable to create it. Can anyone help me. Here is the fiddle with circle
Here is the code.
<script type="text/paperscript" canvas="canvas">
var raster = new Raster({
source: 'Chrysanthemum.jpg',
position: view.center
});
var path = null;
var circles = [];
var isDrawing = false;
var draggingIndex = -1;
var segment, movePath;
var resize = false;
project.activeLayer.selected = false;
function onMouseDrag(event) {
if (!isDrawing && circles.length > 0) {
for (var ix = 0; ix < circles.length; ix++) {
if (circles[ix].contains(event.point)) {
draggingIndex = ix;
break;
}
}
}
if (draggingIndex > -1) {
circles[draggingIndex].position = event.point;
} else {
path = new Path.Circle({
center: event.point,
radius: (event.downPoint - event.point).length,
fillColor: null,
strokeColor: 'black',
strokeWidth: 10
});
path.removeOnDrag();
isDrawing = true;
}
}
;
function onMouseUp(event) {
if (isDrawing) {
circles.push(path);
}
isDrawing = false;
draggingIndex = -1;
}
;
function onMouseMove(event) {
project.activeLayer.selected = false;
if (event.item)
event.item.selected = true;
resize = true;
}
var segment, path;
var movePath = false;
function onMouseDown(event) {
segment = path = null;
var hitResult = project.hitTest(event.point, hitOptions);
if (!hitResult)
return;
if (hitResult) {
path = hitResult.item;
if (hitResult.type == 'segment') {
segment = hitResult.segment;
} else if (hitResult.type == 'stroke') {
var location = hitResult.location;
segment = path.insert(location.index + 1, event.point);
path.smooth();
}
}
movePath = hitResult.type == 'fill';
if (movePath)
project.activeLayer.addChild(hitResult.item);
}
</script>
First, your code (on jsfiddle) does not run.
The paperjs external resource returned a 404. https://raw.github.com/paperjs/paper.js/master/dist/paper.js works for paperjs.
The raster source was for a local file, not a URI.
In onMouseDown, project.hitTest references an undefined hitOptions.
It seems from your question that you want to be able to drag the circle segments to resize the circle, and you tried using two onMouseDrag functions to do that, which would not work. Instead, both operations should be in the same onMouseDrag, using if-then-else to choose between them. To make this work as expected, the item that was hit should be stored in onMouseDown instead of whatever circle your code finds at the beginning of onMouseDrag. For example, here onMouseDrag can either "move" or "resize" (jsfiddle here):
<script type="text/paperscript" canvas="myCanvas">
var raster = new Raster({
source: 'http://i140.photobucket.com/albums/r10/Array39/Chrysanthemum.jpg',
position: view.center
});
var circles = [];
var hitItem = null;
var currentAction = null;
function onMouseMove(event) {
project.activeLayer.selected = false;
if (event.item) {
event.item.selected = true;
}
}
function onMouseDown(event) {
hitItem = null;
var aColor = new Color('black');
for (var i = 0; i < circles.length; i++) {
circles[i].fillColor = aColor;
}
view.draw();
var hitResult = project.hitTest(event.point);
for (var i = 0; i < circles.length; i++) {
circles[i].fillColor = null;
}
view.draw();
if (!hitResult) {
return; //only happens if we don't even hit the raster
}
hitItem = hitResult.item;
if (circles.indexOf(hitItem) < 0) {
var newCircle = new Path.Circle({
center: event.point,
radius: 2,
strokeColor: 'black',
strokeWidth: 10
});
hitItem = newCircle;
circles.push(hitItem);
currentAction = 'resize';
return;
}
if (hitResult.type == 'segment') {
currentAction = 'resize';
} else if (hitResult.type == 'stroke') {
hitItem.insert(hitResult.location.index + 1, event.point);
hitItem.smooth();
currentAction = 'resize';
} else if (hitResult.type == 'fill') {
currentAction = 'move';
}
}
function onMouseDrag(event) {
if (!hitItem) {
return;
}
if (currentAction == 'move') {
hitItem.position = event.point;
} else if (currentAction == 'resize') {
if ((event.downPoint - event.point).length >= 1) {
hitItem.fitBounds(new Rectangle(event.downPoint, event.point), true);
}
}
};
</script>
<canvas id="myCanvas"></canvas>
Also note:
In onMouseDown, the function returns if !hitResult, so you do not need to test if (hitResult) right after that return.
Naming variables the same as objects makes searching more difficult, e.g., in your code path is an instance of Path.
Using the same variable for different purposes makes code more difficult to parse, e.g., in your code path is used to create new circles as well as to store which circle has been selected.
You have multiple variables defined twice: path, movePath, and segment.
If a variable will only be used in a single function, e.g., movePath and segment, then it makes the code more readable if the variable is defined in that function. Also, movePath is only used in a single if-statement, which just adds items back to the layer, but the only items not in the layer have been removed when the circle was originally drawn. Since those items cannot be hit, the item that was hit must already be in the layer.
The variable segment is not used.
It makes the code flow/read better if the functions are ordered logically. In this case, onMouseMove should go first because it happens before the button is clicked. Then onMouseDown goes next because it must happen before the other actions. Then onMouseDrag, and finally onMouseUp.
Instead of creating new circles in onMouseDrag and then throwing them away on the next drag, it makes more sense to create one in onMouseDown if there was no item hit, or if the hit item is not a circle. Then in onMouseDown, you just resize that circle. Path.scale or Path.fitBounds can be used for such resizing.
Instead of using multiple boolean variables to keep track of the current action (e.g., resize vs move), it is more logical to have a single variable keeping track of the current action.
Instead of your code to find whether the point is within a circle, the code I am using temporarily sets the circles' fillColor, do the hitTest, and then clears the circles' fillColor. I did this because when you hit a stroke, the shape of the circle changes, for which your code to find the draggingIndex does not account.

Show a moving marker on the map

I am trying to make a marker move(not disappear and appear again) on the map as a vehicle moves on the road.
I have two values of latLng and I want to move the marker between the two till the next point is sent by the vehicle. And then repeat the process again.
What I tried:[This is not a very efficient way, I know]
My thought was to implement the above using the technique in points below:
1) Draw a line between the two.
2) Get the latLng of each point on 1/10th fraction of the polyline.
3) Mark the 10 points on the map along with the polyline.
Here is my Code:
var isOpen = false;
var deviceID;
var accountID;
var displayNameOfVehicle;
var maps = {};
var lt_markers = {};
var lt_polyLine = {};
function drawMap(jsonData, mapObj, device, deleteMarker) {
var oldposition = null;
var oldimage = null;
var arrayOflatLng = [];
var lat = jsonData[0].latitude;
var lng = jsonData[0].longitude;
//alert(jsonData[0].imagePath);
var myLatLng = new google.maps.LatLng(lat, lng);
if (deleteMarker == true) {
if (lt_markers["marker" + device] != null) {
oldimage = lt_markers["marker" + device].getIcon().url;
oldposition = lt_markers["marker" + device].getPosition();
lt_markers["marker" + device].setMap(null);
lt_markers["marker" + device] = null;
}
else {
console.log('marker is null');
oldimage = new google.maps.MarkerImage(jsonData[0].imagePath,
null,
null,
new google.maps.Point(5, 17), //(15,27),
new google.maps.Size(30, 30));
oldposition = myLatLng;
}
}
var image = new google.maps.MarkerImage(jsonData[0].imagePath,
null,
null,
new google.maps.Point(5, 17), //(15,27),
new google.maps.Size(30, 30));
lt_markers["marker" + device] = new google.maps.Marker({
position: myLatLng,
icon: image,
title: jsonData[0].address
});
if (oldposition == myLatLng) {
alert('it is same');
lt_markers["marker" + device].setMap(mapObj);
mapObj.panTo(myLatLng);
}
else {
alert('it is not same');
var markMarker = null;
var i = 10;
for (i = 10; i <= 100; i + 10) {
//-------
// setTimeout(function() {
if (markMarker != null) {
markMarker.setMap(null);
markMarker = null;
}
alert('inside the loop');
var intermediatelatlng = mercatorInterpolate(mapObj, oldposition, myLatLng, i / 100);
alert('Intermediate Latlng is :' + intermediatelatlng);
arrayOflatLng.push(intermediatelatlng);
var flightPath = new google.maps.Polyline({
path: arrayOflatLng,
strokeColor: "#FFFFFF",
strokeOpacity: 1.0,
strokeWeight: 1
});
flightPath.setMap(mapObj);
if (i != 100) {
markMarker = new google.maps.Marker({
position: intermediatelatlng,
icon: image,
title: jsonData[0].address,
map: mapObj
});
}
else {
markMarker = new google.maps.Marker({
position: intermediatelatlng,
icon: oldimage,
title: jsonData[0].address,
map: mapObj
});
}
mapObj.panTo(intermediatelatlng);
//--------
// }, 1000);
}
}
}
function mercatorInterpolate(map, latLngFrom, latLngTo, fraction) {
// Get projected points
var projection = map.getProjection();
var pointFrom = projection.fromLatLngToPoint(latLngFrom);
var pointTo = projection.fromLatLngToPoint(latLngTo);
// Adjust for lines that cross the 180 meridian
if (Math.abs(pointTo.x - pointFrom.x) > 128) {
if (pointTo.x > pointFrom.x)
pointTo.x -= 256;
else
pointTo.x += 256;
}
// Calculate point between
var x = pointFrom.x + (pointTo.x - pointFrom.x) * fraction;
var y = pointFrom.y + (pointTo.y - pointFrom.y) * fraction;
var pointBetween = new google.maps.Point(x, y);
// Project back to lat/lng
var latLngBetween = projection.fromPointToLatLng(pointBetween);
return latLngBetween;
}
Problems Faced:
1) The marker is not showing up on the map because the process of plotting and removal of marker is so fast that the marker is not visisble on screen. I've tried setTimeOut, and It does not help at all.
2) if I alow the browser to run this code for more than 5 minutes, the browser crashes.
Note: The Above function is called every 10 seconds using setInterval.
What Can be a better solution? Please Help..
For the marker to move relatively smoothly, you need to
Update more than every 1/10 fraction of the polyline (at least every few pixels)
Call the update method more frequently
Don't delete and re-add the marker
For example, something like:
var counter = 0;
interval = window.setInterval(function() {
counter++;
// just pretend you were doing a real calculation of
// new position along the complex path
var pos = new google.maps.LatLng(35, -110 + counter / 100);
marker.setPosition(pos);
if (counter >= 1000) {
window.clearInterval(interval);
}
}, 10);
I made a simple example at http://jsfiddle.net/bmSbU/2/ which shows a marker moving along a straight path. If this is what you want, most of your code above regarding where along the line you are can be reused (or check out http://broady.github.io/maps-examples/points-along-line/along-directions.html )
You can use marker-animate-unobtrusive library to make markers
smoothly transition from one location to another (instead of reappearing).
You could initialize your marker like that:
var marker = new SlidingMarker({
//your original marker options
});
Just call marker.setPosition() each time new vehicle's coordinate arrive.
P.S. I'm the author of the library.
Why not keep the existing Marker/ MarkerImage and call setPosition() to move it, either on a timer or as the position changes?
Deleting it & recreating it is what causes it to flash/ flicker and eventually crash. If you keep the same instance but just move it, you should do much better.
See: Marker.setPosition()
https://developers.google.com/maps/documentation/javascript/reference#Marker

Using Google Maps custom overlays to create custom icons (with Raphael JS)

I'm using Google Maps OverlayView class to create custom markers (with Raphael JS) and am having issues accessing certain properties of my new subclass when calling a public method.
I followed Google's fairly straightforward example here ~ https://developers.google.com/maps/documentation/javascript/overlays#CustomOverlays ~ to create a custom marker class, including its 'hide' and 'show' methods.
function MapCustomMarker(opts){
this.pos_ = opts.position;
this.map_ = opts.map;
this.div_ = null;
this.color_ = (!opts.color ? '#e32636' : opts.color);
this.height_ = 32;
this.width_ = 32;
this.scale_ = 1.2;
this.icons_ = {
pinpoint:'M16,3.5c-4.142,0-7.5,3.358-7.5,7.5c0,4.143,7.5,18.121,7.5,18.121S23.5,15.143,23.5,11C23.5,6.858,20.143,3.5,16,3.5z M16,14.584c-1.979,0-3.584-1.604-3.584-3.584S14.021,7.416,16,7.416S19.584,9.021,19.584,11S17.979,14.584,16,14.584z',
help: 'M12.558,15.254c2.362,0,4.277-1.916,4.277-4.279s-1.916-4.279-4.277-4.279c-2.363,0-4.28,1.916-4.28,4.279S10.194,15.254,12.558,15.254zM15.662,15.224c-0.875,0.641-1.941,1.031-3.103,1.031c-1.164,0-2.231-0.391-3.105-1.031c-0.75,0.625-1.498,1.519-2.111,2.623c-1.422,2.563-1.578,5.192-0.35,5.874c0.55,0.312,1.127,0.078,1.723-0.496c-0.105,0.582-0.166,1.213-0.166,1.873c0,2.938,1.139,5.312,2.543,5.312c0.846,0,1.265-0.865,1.466-2.188c0.201,1.311,0.62,2.188,1.462,2.188c1.396,0,2.544-2.375,2.544-5.312c0-0.66-0.062-1.291-0.167-1.873c0.598,0.574,1.174,0.812,1.725,0.496c1.228-0.682,1.069-3.311-0.353-5.874C17.159,16.742,16.412,15.849,15.662,15.224zM19.821,3.711l-1.414,1.414c1.499,1.499,2.428,3.569,2.428,5.851c0,2.283-0.929,4.353-2.428,5.853l1.413,1.412c1.861-1.86,3.015-4.43,3.015-7.265C22.835,8.142,21.683,5.572,19.821,3.711zM16.288,14.707l1.413,1.414c1.318-1.318,2.135-3.138,2.135-5.145c0-2.007-0.816-3.827-2.134-5.145l-1.414,1.414c0.956,0.956,1.547,2.275,1.547,3.731S17.243,13.751,16.288,14.707zM21.941,1.59l-1.413,1.414c2.042,2.042,3.307,4.862,3.307,7.971c0,3.11-1.265,5.93-3.308,7.972l1.413,1.414c2.405-2.404,3.895-5.725,3.895-9.386C25.835,7.315,24.346,3.995,21.941,1.59z'
}
this.popup_ = 'M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z';
this.icon_ = this.icons_[opts.icon];
this.setMap(opts.map);
}
MapCustomMarker.prototype = new google.maps.OverlayView();
MapCustomMarker.prototype.onAdd = function() {
// Create the DIV and set some basic attributes.
var div = document.createElement('div');
div.style.border = "none";
div.style.borderWidth = "0px";
div.style.position = "absolute";
div.style.cursor = "pointer";
div.style.width = this.width_+"px";
div.style.height = this.height_+"px";
var paper = Raphael(div,this.height_, this.width_);
var el = paper.path(Raphael.transformPath(this.icon_, 's'+this.scale_)).attr({fill: this.color_, stroke: "#333333"});
// Set the overlay's div_ property to this DIV
this.div_ = div;
// We add an overlay to a map via one of the map's panes.
// We'll add this overlay to the overlayImage pane.
var panes = this.getPanes();
panes.overlayMouseTarget.appendChild(div);
}
MapCustomMarker.prototype.draw = function() {
// Size and position the overlay.
var overlayProjection = this.getProjection();
// We'll use these coordinates to position the DIV.
var o = overlayProjection.fromLatLngToDivPixel(this.pos_);
var l = o.x - Math.round(this.width_ / 2);
var t = o.y - this.height_;
this.div_.style.left = l + 'px';
this.div_.style.top = t + 'px';
}
MapCustomMarker.prototype.onRemove = function() {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
MapCustomMarker.prototype.hide = function() {
console.log(this.div_);
console.log(this.color_);
if (this.div_) {
this.div_.style.visibility = "hidden";
}
}
MapCustomMarker.prototype.show = function() {
if (this.div_) {
this.div_.style.visibility = "visible";
}
}
MapCustomMarker.prototype.toggle = function() {
if (this.div_) {
if (this.div_.style.visibility == "hidden") {
this.show();
} else {
this.hide();
}
}
}
This class creates markers on my map with the Raphael icons very nicely.
The problem comes when I want to hide or show any specific marker.
var marker = new MapCustomMarker({position: pos, map: self.map, icon:'help', color:'#e32636'});
marker.hide();
marker.hide() is not hiding the markers.
You'll notice in the "hide" method, I have two console.log commands testing the values of this.color_ and this.div_. console.log(this.color_) returns the color set when the object is initiated. console.log(this.div_) returns null even though it was obviously altered in the 'onAdd' and 'draw' methods when the marker was created.
I'm not sure if this is a misunderstanding of public and private properties in javascript or something else. I used the google maps custom overlay example almost exactly.
If anyone has any ideas, please pass them along. (And aside from this one issue, I hope this code will assist others who want to merge Raphael JS capabilities with Google Maps.)
Thanks!
So it appears that marker.hide() is being called before the methods 'onAdd' and 'draw' have a chance to alter the this.div_ variable when the object is instantiated. Since I want all markers to be hidden when they are created, I'm just going to add
div.style.visibility = "hidden";
to the onAdd method, and will call maker.show() later, well after all of the markers have been loaded.

Google maps v3 polyline tooltip

A google maps marker object (google.maps.Marker) has a title property, so when a user moves their mouse over the marker a simple tooltip is displayed.
There isn't a title property on a polyline (google.maps.Polyline). Is there a way I can do this / simulate this in V3? I could do this in V2, and I can't find an example for V3.
I combined #samshull's answer above (duly upvoted!) with info from here to make the InfoWindow appear where the user's cursor mouses over the line:
// Open the InfoWindow on mouseover:
google.maps.event.addListener(line, 'mouseover', function(e) {
infoWindow.setPosition(e.latLng);
infoWindow.setContent("You are at " + e.latLng);
infoWindow.open(map);
});
// Close the InfoWindow on mouseout:
google.maps.event.addListener(line, 'mouseout', function() {
infoWindow.close();
});
Here, line is your PolyLine object; map is your Map object; and infoWindow is your InfoWindow object, which I just create with:
var infoWindow = new google.maps.InfoWindow();
I also follow this advice by re-using the same InfoWindow object for all my polylines rather than creating a new one for each line:
Best practices: For the best user experience, only one info window
should be open on the map at any one time. Multiple info windows make
the map appear cluttered. If you only need one info window at a time,
you can create just one InfoWindow object and open it at different
locations or markers upon map events, such as user clicks. If you do
need more than one info window, you can display multiple InfoWindow
objects at the same time.
Note that infoWindow.setContent() takes a string. So call toString() on a number variable if you want to display a number in the InfoWindow.
I view all of this as an imperfect workaround until Google Maps hopefully one day add a title property to PolylineOptions, just like they've already done for MarkerOptions.
I am not 100% this is the only way, or the best way, but it is a way to create a window over your Polyline
In Google maps V3, you should to create an InfoWindow then set the content using myInfoWindow.setContent("Hello World!")
In order to make it show on mouseover, you will need to do something like:
google.maps.event.addListener(myPolyline, 'mouseover', function() {
myInfoWindow.open(mymap);
// mymap represents the map you created using google.maps.Map
});
// assuming you want the InfoWindow to close on mouseout
google.maps.event.addListener(myPolyline, 'mouseout', function() {
myInfoWindow.close();
});
I found this online that helped me do tooltips on polygons,
from http://philmap.000space.com/gmap-api/poly-hov.html:
var tooltip=function(){
var id = 'tt';
var top = 3;
var left = 3;
var maxw = 200;
var speed = 10;
var timer = 20;
var endalpha = 95;
var alpha = 0;
var tt,t,c,b,h;
var ie = document.all ? true : false;
return{
show:function(v,w){
if(tt == null){
tt = document.createElement('div');
tt.setAttribute('id',id);
t = document.createElement('div');
t.setAttribute('id',id + 'top');
c = document.createElement('div');
c.setAttribute('id',id + 'cont');
b = document.createElement('div');
b.setAttribute('id',id + 'bot');
tt.appendChild(t);
tt.appendChild(c);
tt.appendChild(b);
document.body.appendChild(tt);
tt.style.opacity = 0;
tt.style.filter = 'alpha(opacity=0)';
document.onmousemove = this.pos;
}
tt.style.visibility = 'visible';
tt.style.display = 'block';
c.innerHTML = v;
tt.style.width = w ? w + 'px' : 'auto';
if(!w && ie){
t.style.display = 'none';
b.style.display = 'none';
tt.style.width = tt.offsetWidth;
t.style.display = 'block';
b.style.display = 'block';
}
if(tt.offsetWidth > maxw){tt.style.width = maxw + 'px'}
h = parseInt(tt.offsetHeight) + top;
clearInterval(tt.timer);
tt.timer = setInterval(function(){tooltip.fade(1)},timer);
},
pos:function(e){
var u = ie ? event.clientY + document.documentElement.scrollTop : e.pageY;
var l = ie ? event.clientX + document.documentElement.scrollLeft : e.pageX;
tt.style.top = (u - h) + 'px';
tt.style.left = (l + left) + 'px';
},
fade:function(d){
var a = alpha;
if((a != endalpha && d == 1) || (a != 0 && d == -1)){
var i = speed;
if(endalpha - a < speed && d == 1){
i = endalpha - a;
}else if(alpha < speed && d == -1){
i = a;
}
alpha = a + (i * d);
tt.style.opacity = alpha * .01;
tt.style.filter = 'alpha(opacity=' + alpha + ')';
}else{
clearInterval(tt.timer);
if(d == -1){tt.style.display = 'none'}
}
},
hide:function(){
clearInterval(tt.timer);
tt.timer = setInterval(function(){tooltip.fade(-1)},timer);
}
};
}();
Also, Please see this SO discussion about the same topic:
Tooltip over a Polygon in Google Maps
And,
Google maps rectangle/polygon with title
If i'm not mistaken i don't think it is possible to set the tooltip since as you mentioned there is not a title property in PolygonOptions object.But you can make a div that looks exactly the same as the tooltip and place it let's say in the tip of your mouse during the mousemove event.I tried also to find a solution to place this tooltip somewhere in the center of the polygon but i think it is too much of a trouble that's why i also think the google guys didn't implement it also.
Cheers

Categories

Resources