I have a script which on button click finds the closest location to you by searching an array, it then finds the corresponding entry in another array and adds a number to that array. However I am having issues with appending the value to it. Instead of starting with "0" and then adding 1 making it "1" it comes up like this "01" I thought it was because it was a string to I tried to parse it as an Int this didn't work. I think it may be a problem with the way I am initially setting values in the array, I made a For loop to set the first 200 values to 0, though I only want this to run once then as the button is clicked it appends the number that is typed in to a text field. I am unsure on how to progress with this issue. Code below:
I have tried to highlight the important bits of my code where I think the problem lies.
Screenshot of problem (rather than adding the value I put in the box it just attaches it to the end as if it were a string):
jQuery(function($){
var Loc1;
var Loc2;
var service;
var marker = [];
var pos;
var infowindow;
var placeLoc
**var j;**
**var markerValue = [];**
**for (j = 0; j<200; j++ ){
markerValue[j] = 0;
}**
var markers;
var x = 0, y = 0,
vx = 0, vy = 0,
ax = 0, ay = 0;
var points;
var sphere = document.getElementById("sphere");
var counting = false;
var counter = 0;
var numberOne;
if (window.DeviceMotionEvent != undefined) {
window.ondevicemotion = function(e) {
ax = event.accelerationIncludingGravity.x * 5;
ay = event.accelerationIncludingGravity.y * 5;
document.getElementById("counterSpan").innerHTML = Math.round(counter*10)/10;
//document.getElementById("accelerationX").innerHTML = Math.round(e.accelerationIncludingGravity.x * 10)/10;
//document.getElementById("accelerationY").innerHTML = Math.round(e.accelerationIncludingGravity.y * 10)/10;
//document.getElementById("accelerationZ").innerHTML = Math.round(e.accelerationIncludingGravity.z * 10)/10;
var moveX = Math.round(e.accelerationIncludingGravity.x * 10)/10;
//var moveY = Math.round(e.accelerationIncludingGravity.y * 10)/10;
//var moveZ = Math.round(e.accelerationIncludingGravity.z * 10)/10;
if(moveX > 5 || moveX < -5) {
counting = true;
//alert(counting);
if(counter < 100){counter+=0.01;}
}
if ( e.rotationRate ) {
//document.getElementById("rotationAlpha").innerHTML = Math.round(e.rotationRate.alpha * 10)/10;
//document.getElementById("rotationBeta").innerHTML = Math.round(e.rotationRate.beta * 10)/10;
//document.getElementById("rotationGamma").innerHTML = Math.round(e.rotationRate.gamma * 10)/10;
}
}
setInterval( function() {
var landscapeOrientation = window.innerWidth/window.innerHeight > 1;
if ( landscapeOrientation) {
vx = vx + ay;
vy = vy + ax;
} else {
vy = vy - ay;
vx = vx + ax;
}
vx = vx * 0.98;
vy = vy * 0.98;
y = parseInt(y + vy / 50);
x = parseInt(x + vx / 50);
boundingBoxCheck();
//sphere.style.top = y + "px";
//sphere.style.left = x + "px";
}, 25);
}
function boundingBoxCheck(){
if (x<0) { x = 0; vx = -vx; }
if (y<0) { y = 0; vy = -vy; }
if (x>document.documentElement.clientWidth-20) { x = document.documentElement.clientWidth-20; vx = -vx; }
if (y>document.documentElement.clientHeight-20) { y = document.documentElement.clientHeight-20; vy = -vy; }
}
function minmax(value, min, max)
{
if(parseInt(value) < 0 || isNaN(value))
return 0;
else if(parseInt(value) > 100)
return 100;
else return value;
}
var $overlay = $('.overlay'),
resize = true,
map;
function initialize() {
/*var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(-34.397, 150.644),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
}*/
var mapOptions = {
zoom: 15
};
map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
// Try HTML5 geolocation
if(navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var pos = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
var request = {
location:pos,
center:pos,
radius:1000,
};
infowindow = new google.maps.InfoWindow();
var service = new google.maps.places.PlacesService(map);
service.nearbySearch(request,callback);
pos = new google.maps.LatLng(position.coords.latitude,
position.coords.longitude);
infowindow = new google.maps.InfoWindow({
map: map,
position: pos,
content: 'You Are Here'
});
$('#btnEnergize').data('pos',pos);
map.setCenter(pos);
}, function() {
handleNoGeolocation(true);
});
} else {
// Browser doesn't support Geolocation
handleNoGeolocation(false);
}
function callback(results, status) {
markers = [];
if (status == google.maps.places.PlacesServiceStatus.OK) {
for (var i = 0; i < results.length; i++) {
markers.push(createMarker(results[i]));
}
}
$('#btnEnergize').data('markers',markers);
}
}
function createMarker(place) {
placeLoc = place.geometry.location;
var marker = new google.maps.Marker({
map: map,
position: place.geometry.location,
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 8,
fillColor:'00a14b',
fillOpacity:0.3,
fillStroke: '00a14b',
strokeWeight:4,
strokeOpacity: 0.7
},
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(place.name);
infowindow.open(map, this);
});
return marker;
}
function handleNoGeolocation(errorFlag) {
if (errorFlag) {
var content = 'Error: The Geolocation service failed.';
} else {
var content = 'Error: Your browser doesn\'t support geolocation.';
}
var options = {
map: map,
position: new google.maps.LatLng(60, 105),
content: content
};
var infowindow = new google.maps.InfoWindow(options);
map.setCenter(options.position);
}
google.maps.event.addDomListener(window, 'load', initialize);
$('#show').click(function(){
$overlay.show();
if ( resize ){
google.maps.event.trigger(map, 'resize');
resize = false;
}
});
$('.overlay-bg').click(function(){
$overlay.hide();
});
**$( "#btnEnergize" ).click(function() {
var pos = $(this).data('pos'),
markers = $(this).data('markers'),
closest;
if(!pos || !markers){
return;
}
$.each(markers,function(){
var distance=google.maps.geometry.spherical
.computeDistanceBetween(this.getPosition(),pos);
if(!closest || closest.distance > distance){
closest={marker:this,
distance:distance}
}
});
if(closest){
//closest.marker will be the nearest marker, do something with it
//here we simply trigger a click, which will open the InfoWindow
google.maps.event.trigger(closest.marker,'click')
Loc2 = closest.marker.getPosition();
numberOne = $("#numberOne").val();
alert(numberOne);
localStorage.setItem('points',numberOne);
for (var i=0; i<markers.length; i++) {
Loc1 = markers[i].getPosition();
if (Loc1.equals(Loc2) /*&& numberOne <= counter*/){
console.log("marker array Location 1 " + Loc1 + " marker " + i);
console.log("closest Location 2 " +Loc2);
counter -= numberOne;
markerValue[i] += numberOne;
console.log( "marker " + i + " now equals " + markerValue[i]);
}
}
}
alert (markerValue[19]);
});**
});// JavaScript Document
From skimming over the code it looks like you are retrieving the value here:
numberOne = $("#numberOne").val();
that should be
numberOne = parseInt($("#numberOne").val());
You use parseInt in the function minmax(), but that one is not in use. If this was the one you used to parse the value to int it has one flaw in that it will only return parsed value on 0 and 100:
function minmax(value, min, max)
{
// Flip these two, CHECK FOR isNaN first.
if(parseInt(value) < 0 || isNaN(value))
return 0;
else if(parseInt(value) > 100)
return 100;
else return value; // Here you return value, not parseInt(value)
}
Related
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 allow user to rotate bitmap features on map with OpenLayers.Control.ModifyFeature or another way as it work for Polygons or other geometry objects, except Point, but only for Point I can set "externalGraphic" with my bitmap. Example of ModifyFeature to rotation as I expected here: ModifyFeature example
When I add Vector with Point geometry and activate ModifyFeature there is no rotation tool showing - only drag-drop. I know what is a point, but I need to have tool for rotate bitmap features. It may be image on any another geometry object, but with custom image.
After long research I found an example in gis.stackexchange.com, and fix it.
Here is a code which works for me:
OpenLayers.Control.RotateGraphicFeature = OpenLayers.Class(OpenLayers.Control.ModifyFeature, {
rotateHandleStyle: null,
initialize: function(layer, options) {
OpenLayers.Control.ModifyFeature.prototype.initialize.apply(this, arguments);
this.mode = OpenLayers.Control.ModifyFeature.ROTATE; // This control can only be used to rotate the feature
this.geometryTypes = ['OpenLayers.Geometry.Point'] // This control can only be used to rotate point because the 'exteralGraphic' is a point style property
var init_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style.select);
this.rotateHandleStyle = OpenLayers.Util.extend(init_style, {
externalGraphic: "./static/resources/images/cross.png",
graphicWidth: 12,
graphicHeight: 12,
fillOpacity: 1
});
},
resetVertices: function() {
// You need to set yours renderIntent or use "vertex"
if (this.feature && this.feature.renderIntent == "vertex") {
var vertex = this.feature;
this.feature = this.backup_feature;
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
this.collectRadiusHandle();
return;
}
if (this.dragControl.feature) {
this.dragControl.outFeature(this.dragControl.feature);
}
if (this.vertices.length > 0) {
this.layer.removeFeatures(this.vertices, {silent: true});
this.vertices = [];
}
if (this.virtualVertices.length > 0) {
this.layer.removeFeatures(this.virtualVertices, {silent: true});
this.virtualVertices = [];
}
if (this.dragHandle) {
this.layer.destroyFeatures([this.dragHandle], {silent: true});
this.dragHandle = null;
}
if (this.radiusHandle) {
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
this.radiusHandle = null;
}
if (this.feature && this.feature.geometry &&
this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
this.collectDragHandle();
}
if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
OpenLayers.Control.ModifyFeature.RESIZE))) {
this.collectRadiusHandle();
}
if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) {
if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) {
this.collectVertices();
}
}
}
this.collectRadiusHandle();
},
collectRadiusHandle: function() {
var scope = this,
feature = this.feature,
geometry = this.feature.geometry || this.backup_feature.geometry,
centroid = geometry.getCentroid().transform(this.WGS84_google_mercator, this.WGS84),
lon = centroid.x, lat = centroid.y;
if (this.feature.geometry) {
this.backup_feature = this.feature;
} else {
this.feature.geometry = this.backup_feature.geometry;
}
var originGeometry = new OpenLayers.Geometry.Point(lon, lat);
// radius geometry position.
var pixel_dis_x = 10,
pixel_dis_y = -10;
var rotationFeatureGeometry = new OpenLayers.Geometry.Point(lon+pixel_dis_x, lat+pixel_dis_y);
var rotationFeature = new OpenLayers.Feature.Vector(rotationFeatureGeometry, null, this.rotateHandleStyle);
var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);
var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
rotationFeatureGeometry.move = function(x, y) {
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
var dx1 = this.x - originGeometry.x;
var dy1 = this.y - originGeometry.y;
var dx0 = dx1 - x;
var dy0 = dy1 - y;
if (rotate) {
var a0 = Math.atan2(dy0, dx0);
var a1 = Math.atan2(dy1, dx1);
var angle = a1 - a0;
angle *= 180 / Math.PI;
var old_angle = feature.attributes.angle;
var new_angle = old_angle - angle;
feature.attributes.angle = new_angle;
// redraw the feature
scope.feature.layer.redraw.call(scope.feature.layer);
}
};
rotationFeature._sketch = true;
this.radiusHandle = rotationFeature;
this.radiusHandle.renderIntent = this.vertexRenderIntent;
this.layer.addFeatures([this.radiusHandle], {silent: true});
},
CLASS_NAME: "OpenLayers.Control.RotateGraphicFeature"
});
Style of your Vector may be as:
new OpenLayers.StyleMap({
"default": new OpenLayers.Style({
externalGraphic: "link/to/icon",
graphicHeight: "32px",
graphicWidth: "25px",
fillOpacity: 1,
rotation: "${angle}",
graphicZIndex: 1
})
})
UPD: I fixed it for OpenLayers 2.13.1
OpenLayers.Control.RotateGraphicFeature = OpenLayers.Class(OpenLayers.Control.ModifyFeature, {
rotateHandleStyle: null,
initialize: function (layer, options) {
OpenLayers.Control.ModifyFeature.prototype.initialize.apply(this, arguments);
this.mode = OpenLayers.Control.ModifyFeature.ROTATE; // This control can only be used to rotate the feature
this.geometryTypes = ['OpenLayers.Geometry.Point'] // This control can only be used to rotate point because the 'exteralGraphic' is a point style property
var init_style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style.select);
this.rotateHandleStyle = OpenLayers.Util.extend(init_style, {
externalGraphic: "./static/resources/images/cross.png",
graphicWidth: 12,
graphicHeight: 12,
fillOpacity: 1
});
},
resetVertices: function () {
// You need to set yours renderIntent or use "vertex"
if (this.feature && this.feature.renderIntent == "vertex") {
var vertex = this.feature;
this.feature = this.backup_feature;
if (this.dragControl.feature) {
this.dragControl.outFeature(this.dragControl.feature);
}
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
delete this.radiusHandle;
this.collectRadiusHandle();
return;
}
if (this.vertices.length > 0) {
this.layer.removeFeatures(this.vertices, {silent: true});
this.vertices = [];
}
if (this.virtualVertices.length > 0) {
this.layer.removeFeatures(this.virtualVertices, {silent: true});
this.virtualVertices = [];
}
if (this.dragHandle) {
this.layer.destroyFeatures([this.dragHandle], {silent: true});
this.dragHandle = null;
}
if (this.radiusHandle) {
this.layer.destroyFeatures([this.radiusHandle], {silent: true});
this.radiusHandle = null;
}
if (this.feature && this.feature.geometry &&
this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
this.collectDragHandle();
}
if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
OpenLayers.Control.ModifyFeature.RESIZE))) {
this.collectRadiusHandle();
}
if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) {
if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) {
this.collectVertices();
}
}
}
this.collectRadiusHandle();
},
collectRadiusHandle: function () {
var scope = this,
feature = this.feature,
data = feature.attributes,
geometry = this.feature.geometry || this.backup_feature.geometry,
center = this.feature.geometry.bounds.getCenterLonLat();
centroid = geometry.getCentroid().transform(this.WGS84_google_mercator, this.WGS84),
lon = centroid.x, lat = centroid.y;
if (data.type && Tms.settings.roadObjectTypeSettings[data.type].NoAzimuth) {
return;
}
if (this.feature.geometry) {
this.backup_feature = this.feature;
} else {
this.feature.geometry = this.backup_feature.geometry;
}
var originGeometry = new OpenLayers.Geometry.Point(lon, lat);
var center_px = this.map.getPixelFromLonLat(center);
// you can change this two values to get best radius geometry position.
var pixel_dis_x = 20,
pixel_dis_y = 20;
var radius_px = center_px.add(pixel_dis_x, pixel_dis_y);
var rotation_lonlat = this.map.getLonLatFromPixel(radius_px);
var rotationFeatureGeometry = new OpenLayers.Geometry.Point(
rotation_lonlat.lon, rotation_lonlat.lat
);
var rotationFeature = new OpenLayers.Feature.Vector(rotationFeatureGeometry, null, this.rotateHandleStyle);
var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);
var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
rotationFeatureGeometry.move = function(x, y) {
OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
var dx1 = this.x - originGeometry.x;
var dy1 = this.y - originGeometry.y;
var dx0 = dx1 - x;
var dy0 = dy1 - y;
if (rotate) {
var a0 = Math.atan2(dy0, dx0);
var a1 = Math.atan2(dy1, dx1);
var angle = a1 - a0;
angle *= 180 / Math.PI;
var old_angle = feature.attributes.angle;
var new_angle = old_angle - angle;
feature.attributes.angle = new_angle;
// redraw the feature
scope.feature.layer.redraw.call(scope.feature.layer);
}
};
rotationFeature._sketch = true;
this.radiusHandle = rotationFeature;
this.radiusHandle.renderIntent = this.vertexRenderIntent;
this.layer.addFeatures([this.radiusHandle], {silent: true});
},
CLASS_NAME: "OpenLayers.Control.RotateGraphicFeature"
});
I want to draw a polygon around a polyline. The polyline in my case is a Google Maps direction and I need to show a polygon around it within the Google Maps canvas.
First:
For offsetting I use the JavaScript Clipper Library. I have the following polyline (route): I make an offset polygon below using Clipper:
I have a working JS Bin example.
The code is:
<html>
<head>
<title>Javascript Clipper Library / Offset polyline</title>
<script src="clipper.js"></script>
<script>
function draw() {
var polygons = [[{"X":72,"Y":59.45},{"X":136,"Y":66},{"X":170,"Y":99},{"X":171,"Y":114},{"X":183,"Y":125},{"X":218,"Y":144},{"X":218,"Y":165},{"X":226,"Y":193},{"X":254,"Y":195},{"X":283,"Y":195},{"X":292,"Y":202},{"X":325,"Y":213},{"X":341,"Y":234},{"X":397,"Y":245},{"X":417,"Y":248}]];
var scale = 100;
reverse_copy(polygons);
polygons = scaleup(polygons, scale);
var cpr = new ClipperLib.Clipper();
var delta = 25;
var joinType = ClipperLib.JoinType.jtRound;
var miterLimit = 2;
var AutoFix = true;
var svg, offsetted_polygon,
cont = document.getElementById('svgcontainer');
offsetted_polygon = cpr.OffsetPolygons(polygons, delta * scale, joinType, miterLimit, AutoFix);
//console.log(JSON.stringify(offsetted_polygon));
// Draw red offset polygon
svg = '<svg style="margin-top:10px;margin-right:10px;margin-bottom:10px;background-color:#dddddd" width="540" height="340">';
svg += '<path stroke="red" fill="red" stroke-width="2" stroke-opacity="0.6" fill-opacity="0.2" d="' + polys2path(offsetted_polygon, scale) + '"/>';
//Draw blue polyline
svg += '<path stroke="blue" stroke-width="3" d="' + polys2path(polygons, scale) + '"/>';
svg += '</svg>';
cont.innerHTML += svg;
}
// helper function to scale up polygon coordinates
function scaleup(poly, scale) {
var i, j;
if (!scale)
scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
poly[i][j].X *= scale;
poly[i][j].Y *= scale;
}
}
return poly;
}
// converts polygons to SVG path string
function polys2path (poly, scale) {
var path = "", i, j;
if (!scale)
scale = 1;
for(i = 0; i < poly.length; i++) {
for(j = 0; j < poly[i].length; j++) {
if (!j)
path += "M";
else
path += "L";
path += (poly[i][j].X / scale) + ", " + (poly[i][j].Y / scale);
}
path += "Z";
}
return path;
}
function reverse_copy(poly) {
// Make reverse copy of polygons = convert polyline to a 'flat' polygon ...
var k, klen = poly.length, len, j;
for (k = 0; k < klen; k++) {
len = poly[k].length;
poly[k].length = len * 2 - 2;
for (j = 1; j <= len - 2; j++) {
poly[k][len - 1 + j] = {
X: poly[k][len - 1 - j].X,
Y: poly[k][len - 1 - j].Y
}
}
}
}
</script>
</head>
<body onload="draw()">
<h2>Javascript Clipper Library / Offset polyline</h2>
This page shows an example of offsetting polyline and drawing it using SVG.
<div id="svgcontainer"></div>
</body>
</html>
And all this is good but now I must replace the polygon variables with points from Google Maps directions, so I do this change:
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
function draw() {
var polygons = response.routes[0].overview_path;
//REST OF CODE
}
}
}
I have a JS Bin example with this code for offsetting the polygon around the polyline.
But there is some problem, which I can't regonize and I can't get a polygon around directions.
Is there any way to solve this problem?
My working solution: working example (based off of Manolis Xountasis's answer) and pieces from these related questions:
How to calculate intersection area in Google Maps API with JSTS Library?
Google Maps Polygons self intersecting detection
include the JSTS library
add routines to translate google.maps.Polyline paths to JSTS objects:
function googleMaps2JTS(boundaries) {
var coordinates = [];
var length = 0;
if (boundaries && boundaries.getLength) length = boundaries.getLength();
else if (boundaries && boundaries.length) length = boundaries.length;
for (var i = 0; i < length; i++) {
if (boundaries.getLength) coordinates.push(new jsts.geom.Coordinate(
boundaries.getAt(i).lat(), boundaries.getAt(i).lng()));
else if (boundaries.length) coordinates.push(new jsts.geom.Coordinate(
boundaries[i].lat(), boundaries[i].lng()));
}
return coordinates;
};
and back to google.maps.LatLng arrays
var jsts2googleMaps = function (geometry) {
var coordArray = geometry.getCoordinates();
GMcoords = [];
for (var i = 0; i < coordArray.length; i++) {
GMcoords.push(new google.maps.LatLng(coordArray[i].x, coordArray[i].y));
}
return GMcoords;
}
get the directions polyline from the DirectionsService and buffer it
directionsService.route(request, function (response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var overviewPath = response.routes[0].overview_path,
overviewPathGeo = [];
for (var i = 0; i < overviewPath.length; i++) {
overviewPathGeo.push(
[overviewPath[i].lng(), overviewPath[i].lat()]);
}
var distance = 10 / 111.12, // Roughly 10km
geoInput = {
type: "LineString",
coordinates: overviewPathGeo
};
var geoInput = googleMaps2JTS(overviewPath);
var geometryFactory = new jsts.geom.GeometryFactory();
var shell = geometryFactory.createLineString(geoInput);
var polygon = shell.buffer(distance);
var oLanLng = [];
var oCoordinates;
oCoordinates = polygon.shell.points[0];
for (i = 0; i < oCoordinates.length; i++) {
var oItem;
oItem = oCoordinates[i];
oLanLng.push(new google.maps.LatLng(oItem[1], oItem[0]));
}
if (routePolygon && routePolygon.setMap) routePolygon.setMap(null);
routePolygon = new google.maps.Polygon({
paths: jsts2googleMaps(polygon),
map: map
});
}
});
This is the working solution. You can find the JSTS files at coderwall.com.
var overviewPath = response.routes[0].overview_path,
overviewPathGeo = [];
for (var i = 0; i < overviewPath.length; i++) {
overviewPathGeo.push(
[overviewPath[i].lng(), overviewPath[i].lat()]
);
}
var distance = value / 10000, // Roughly 10km
geoInput = {
type: "LineString",
coordinates: overviewPathGeo
};
var geoReader = new jsts.io.GeoJSONReader(),
geoWriter = new jsts.io.GeoJSONWriter();
var geometry = geoReader.read(geoInput).buffer(distance);
var polygon = geoWriter.write(geometry);
var oLanLng = [];
var oCoordinates;
oCoordinates = polygon.coordinates[0];
for (i = 0; i < oCoordinates.length; i++) {
var oItem;
oItem = oCoordinates[i];
oLanLng.push(new google.maps.LatLng(oItem[1], oItem[0]));
}
var polygone = new google.maps.Polygon({
paths: oLanLng,
map:map
});
This is an alternate solution using Turf.js's buffer module. I have used a Leaflet map to demonstrate the results - but this will work for any mapping library.
var center = [37.78791180770003, -122.40962505340575];
var map = L.map('map').setView(center, 14);;
L.tileLayer(
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18
}).addTo(map);
var line = {
"type": "Feature",
"properties": {
"color": "blue"
},
"geometry": {
"type": "LineString",
"coordinates": [
[-122.40447521209718,
37.79367718768535
],
[-122.40803718566895,
37.79171022624846
],
[-122.40769386291502,
37.79096412372944
],
[-122.40662097930908,
37.789641468930114
],
[-122.40941047668457,
37.789675383451495
],
[-122.40992546081543,
37.78875968591083
],
[-122.40962505340575,
37.78791180770003
]
]
}
};
L.geoJSON(line, {
style: function(feature) {
return {
color: feature.properties.color
};
}
}).addTo(map);
var polygon = turf.buffer(line, 50, {
units: 'meters'
});
L.geoJSON(polygon, {
style: function(feature) {
return {
color: feature.properties.color
};
}
}).addTo(map);
#map {
height: 400px;
}
<script src="https://npmcdn.com/#turf/turf#6.3.0/turf.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.5.1/leaflet.js"></script>
<div id="map"></div>
The Issue
I am using the following example code to draw arrows on google map.
Here is the main overlay function that creates the svg files for the arrow.
// A small directable Arrow overlay for GMaps V3
// Bill Chadwick Feb 2012 after my previous V2 API version
// Free for any use as far as I am concerned
// Some public domain code VML/SVG utility code of Ben Appleton's is reused at the end of this file
var arrowOverlayMarkerCounter; //unique id counter for SVG arrow head markers
function ArrowOverlay(map, location, rotation, color, opacity, tooltip) {
this.map_ = map;
this.location_ = location;
this.rotation_ = rotation || 0.0;
var r = this.rotation_ + 90; //compass to math
this.dx_ = 20 * Math.cos(r * Math.PI / 180); //other end of arrow line to point
this.dy_ = 20 * Math.sin(r * Math.PI / 180);
this.color_ = color || "#0000FF";
this.opacity_ = opacity || 0.7;
this.tooltip_ = tooltip || "";
this.div_ = null;
this.setMap(map);
this.handle_ = null;//click event handler handle
if (arrowOverlayMarkerCounter == null)
arrowOverlayMarkerCounter = 0;
else
arrowOverlayMarkerCounter += 1;
this.svgId_ = "ArrowOverlay" + arrowOverlayMarkerCounter.toString();
}
ArrowOverlay.prototype = new google.maps.OverlayView();
ArrowOverlay.prototype.onAdd = function() {
// Create the DIV and set some basic attributes.
var div = document.createElement('DIV');
div.title = this.tooltip_;
div.style.cursor = "help";
var obj = this;
this.handle_ = google.maps.event.addDomListener(div, 'click', function() { google.maps.event.trigger(obj, "click") });
//set up arrow invariants
if (supportsVML()) {
var l = createVmlElement('v:line', div);
l.strokeweight = "3px";
l.strokecolor = this.color_;
l.style.position = 'absolute';
var s = createVmlElement("v:stroke", l);
s.opacity = this.opacity_;
s.startarrow = "classic"; // or "block", "open" etc see VML spec
this.vmlLine_ = l;
}
else {
// make a 40x40 pixel space centered on the arrow
var svgNS = "http://www.w3.org/2000/svg";
var svgRoot = document.createElementNS(svgNS, "svg");
svgRoot.setAttribute("width", 40);
svgRoot.setAttribute("height", 40);
svgRoot.setAttribute("stroke", this.color_);
svgRoot.setAttribute("fill", this.color_);
svgRoot.setAttribute("stroke-opacity", this.opacity_);
svgRoot.setAttribute("fill-opacity", this.opacity_);
div.appendChild(svgRoot);
var svgNode = document.createElementNS(svgNS, "line");
svgNode.setAttribute("stroke-width", 3);
svgNode.setAttribute("x1", 20);
svgNode.setAttribute("y1", 20);
svgNode.setAttribute("x2", 20 + this.dx_);
svgNode.setAttribute("y2", 20 + this.dy_);
//make a solid arrow head, can't share these, as in SVG1.1 they can't get color from the referencing object, only their parent
//a bit more involved than the VML
if (this.rotation_ >= 0) {
var svgM = document.createElementNS(svgNS, "marker");
svgM.id = this.svgId_;
svgM.setAttribute("viewBox", "0 0 10 10");
svgM.setAttribute("refX", 0);
svgM.setAttribute("refY", 5);
svgM.setAttribute("markerWidth", 4);
svgM.setAttribute("markerHeight", 3);
svgM.setAttribute("orient", "auto");
var svgPath = document.createElementNS(svgNS, "path"); //could share this with 'def' and 'use' but hardly worth it
svgPath.setAttribute("d", "M 10 0 L 0 5 L 10 10 z");
svgM.appendChild(svgPath);
svgRoot.appendChild(svgM);
svgNode.setAttribute("marker-start", "url(#" + this.svgId_ + ")");
}
svgRoot.appendChild(svgNode);
this.svgRoot_ = svgRoot;
this.svgNode_ = svgNode;
}
// Set the overlay's div_ property to this DIV
this.div_ = div;
var panes = this.getPanes();
panes.overlayImage.appendChild(this.div_);
}
ArrowOverlay.prototype.draw = function() {
var overlayProjection = this.getProjection();
var p = overlayProjection.fromLatLngToDivPixel(this.location_);
var div = this.div_;
if (!div)
return;
if (!div.style)
return;
// Calculate the DIV coordinates of the ref point of our arrow
var x2 = p.x + this.dx_;
var y2 = p.y + this.dy_;
if (supportsVML()) {
this.vmlLine_.from = p.x + "px, " + p.y + "px";
this.vmlLine_.to = x2 + "px, " + y2 + "px";
}
else {
this.svgRoot_.setAttribute("style", "position:absolute; top:" + (p.y - 20) + "px; left:" + (p.x - 20) + "px");
}
}
ArrowOverlay.prototype.onRemove = function() {
if (this.handle_ != null) {
google.maps.eventclear.removeListener(this.handle_);
}
this.div_.parentNode.removeChild(this.div_);
}
ArrowOverlay.prototype.setVisible = function(v) {
if (v)
this.show();
else
this.hide();
}
ArrowOverlay.prototype.getVisible = function(v) {
if (this.div_) {
return (this.div_.style.display == "");
}
return false;
}
ArrowOverlay.prototype.hide = function() {
if (this.div_) {
this.div_.style.display = "none";
}
}
ArrowOverlay.prototype.show = function() {
if (this.div_) {
this.div_.style.display = "";
}
}
ArrowOverlay.prototype.setPosition = function(l) {
this.location_ = l;
this.draw();
}
ArrowOverlay.prototype.getPosition = function() {
return this.location_;
}
ArrowOverlay.prototype.setHeading = function(h) {
this.rotation_ = h || 0.0;
var r = this.rotation_ + 90; //compass to math
this.dx_ = 20 * Math.cos(r * Math.PI / 180); //other end of arrow line to point
this.dy_ = 20 * Math.sin(r * Math.PI / 180);
if (!supportsVML()) {
this.svgNode_.setAttribute("x2", 20 + this.dx_);
this.svgNode_.setAttribute("y2", 20 + this.dy_);
}
this.draw();
}
ArrowOverlay.prototype.getHeading = function() {
return this.rotation_;
}
ArrowOverlay.prototype.setTooltip = function(t) {
this.tooltip_ = t;
}
ArrowOverlay.prototype.getTooltip = function() {
return this.tooltip_;
}
ArrowOverlay.prototype.toggle = function() {
if (this.div_) {
if (this.div_.style.visibility == "hidden") {
this.show();
} else {
this.hide();
}
}
}
ArrowOverlay.prototype.fromDivPixelToLatLng = function(x, y) {
var overlayProjection = this.getProjection();
return overlayProjection.fromDivPixelToLatLng(new google.maps.Point(x, y));
}
ArrowOverlay.prototype.fromLatLngToContainerPixel = function(p) {
var overlayProjection = this.getProjection();
return overlayProjection.fromLatLngToContainerPixel(p);
}
// SVG utils from here http://appleton-static.appspot.com/static/simple_poly.js
// by Ben Appleton of Google
var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
function supportsSVG() {
return document.implementation.hasFeature(
'http://www.w3.org/TR/SVG11/feature#Shape',
'1.1');
}
// VML utils from here http://appleton-static.appspot.com/static/simple_poly.js
// by Ben Appleton of Google
var VML_NAMESPACE = 'urn:schemas-microsoft-com:vml';
function createVmlElement(tagName, parent) {
var element = document.createElement(tagName);
parent.appendChild(element);
element.style['behavior'] = 'url(#default#VML)';
return element;
}
function supportsVML() {
if (supportsVML.result_ == null) {
if (!maybeCreateVmlNamespace()) {
return supportsVML.result_ = false;
}
// Create some VML. Its 'adj' property will be an object only when VML
// is enabled.
var div = document.createElement('DIV');
document.body.appendChild(div);
div.innerHtml = '<v:shape id="vml_flag1" adj="1" />';
var child = div.firstChild;
if (child) child.style['behavior'] = 'url(#default#VML)';
supportsVML.result_ = !child || (typeof child['adj'] == 'object');
div.parentNode.removeChild(div);
}
return supportsVML.result_;
}
function maybeCreateVmlNamespace() {
var hasVmlNamespace = false;
if (document.namespaces) {
for (var x = 0; x < document.namespaces.length; x++) {
var ns = document.namespaces(x);
if (ns.name == 'v') {
if (ns.urn == VML_NAMESPACE) {
hasVmlNamespace = true;
} else {
throw new Error('document namespace v: is required for VML ' +
'but has been reserved for ' + ns.urn);
}
}
}
if (!hasVmlNamespace) {
// Import namespace
hasVmlNamespace = true;
document.namespaces.add('v', VML_NAMESPACE);
}
}
return hasVmlNamespace;
}
Question
I can not figure out how to change the length of those arrow? Can I somehow add a length parameter to the function ArrowOverlay?
I would do something like this (scale also works with x, y):
<svg>
<g id="idG" transform="scale(1, 1)">
..your arrow
</g>
</svg>
document.getElementById("idG").setAttribute("transform", "scale(" + horizontalScale + "," + verticalScale + ")");
Example: http://k8.no-ip.org/stackoverflow/13540341.htm
I'm working with a Google Maps example code , this : http://www.wolfpil.de/v3/drag-from-outside.html which has 3 draggable markers outside the map. All I want is to use the revert 'invalid' option to make the markers return to the original position in case the markers do not drop in the map but I've failed to put it in the right line. Could you please give me a hint where do I have to put it to make it work?
function initDrag(e) {
if(!e) var e = window.event;
// Drag image's parent div element
obj = e.target ? e.target.parentNode : e.srcElement.parentElement;
if(obj.className != "drag") {
if(e.cancelable) e.preventDefault();
obj = null;
return;
}
if (obj) {
// The currently dragged object always gets the highest z-index
z_index++;
obj.style.zIndex = z_index.toString();
xpos = e.clientX - obj.offsetLeft;
ypos = e.clientY - obj.offsetTop;
document.onmousemove = moveObj;
}
return false;
}
function moveObj(e) {
if(obj && obj.className == "drag") {
if(!e) var e = window.event;
obj.style.left = e.clientX - xpos + "px";
obj.style.top = e.clientY - ypos + "px";
obj.onmouseup = function() {
var gd = map.getDiv();
var mLeft = gd.offsetLeft;
var mTop = gd.offsetTop;
var mWidth = gd.offsetWidth;
var mHeight = gd.offsetHeight;
var areaLeft = drag_area.offsetLeft;
var areaTop = drag_area.offsetTop;
var oWidth = obj.offsetWidth;
var oHeight = obj.offsetHeight;
// The object's pixel position relative to the document
var x = obj.offsetLeft + areaLeft + oWidth/2;
var y = obj.offsetTop + areaTop + oHeight/2;
// Check if the cursor is inside the map div
if (x > mLeft && x < (mLeft + mWidth) && y > mTop && y < (mTop + mHeight)) {
// Difference between the x property of iconAnchor
// and the middle of the icon width
var anchorDiff = 1;
// Find the object's pixel position in the map container
var g = google.maps;
var pixelpoint = new g.Point(x - mLeft -anchorDiff, y - mTop + (oHeight/2));
// Corresponding geo point on the map
var proj = dummy.getProjection();
var latlng = proj.fromContainerPixelToLatLng(pixelpoint);
// Create a corresponding marker on the map
var src = obj.firstChild.getAttribute("src");
createDraggedMarker(latlng, src);
// Create dragged marker anew
fillMarker();
}
};
}
return false;
}
function fillMarker() {
var m = document.createElement("div");
m.style.position = "absolute";
m.style.width = "32px";
m.style.height = "32px";
var left;
if (obj.id == "m1") {
left = "0px";
} else if (obj.id == "m2") {
left = "50px";
} else if (obj.id == "m3") {
left = "100px";
}
m.style.left = left;
// Set the same id and class attributes again
// m.setAttribute("id", obj.id);
// m.setAttribute((document.all?"className":"class"), "drag");
m.id = obj.id;
m.className = "drag";
// Append icon
var img = document.createElement("img");
img.src = obj.firstChild.getAttribute("src");
img.style.width = "32px";
img.style.height = "32px";
m.appendChild(img);
drag_area.replaceChild(m, obj);
// Clear initial object
obj = null;
}
function highestOrder() {
/**
* The currently dragged marker on the map
* always gets the highest z-index too
*/
return z_index;
}
function createDraggedMarker(point, src) {
var g = google.maps;
var image = new g.MarkerImage(src,
new g.Size(32, 32),
new g.Point(0, 0),
new g.Point(15, 32));
var shadow = new g.MarkerImage("http://maps.gstatic.com/mapfiles/kml/paddle/A_maps.shadow.png",
new g.Size(59, 32),
new g.Point(0, 0),
new g.Point(15, 32));
var marker = new g.Marker({ position: point, map: map,
clickable: true, draggable: true,
raiseOnDrag: false,
icon: image, shadow: shadow, zIndex: highestOrder()
});
g.event.addListener(marker, "click", function() {
actual = marker;
var lat = actual.getPosition().lat();
var lng = actual.getPosition().lng();
iw.setContent(lat.toFixed(6) + ", " + lng.toFixed(6));
iw.open(map, this);
});
g.event.addListener(marker, "dragstart", function() {
// Close infowindow when dragging the marker whose infowindow is open
if (actual == marker) iw.close();
// Increment z_index
z_index++;
marker.setZIndex(highestOrder());
});
}
After the code snippet
fillMarker();
}
add an else statement like this:
fillMarker();
} else {
//if the marker does not land on the map reset marker location
var left;
if (obj.id == "m1") {
left = "0px";
} else if (obj.id == "m2") {
left = "50px";
} else if (obj.id == "m3") {
left = "100px";
}
obj.style.left = left;
obj.style.top = "70px";
}
jsFiddle to full code: http://jsfiddle.net/6ECVs/