I have a script that was written a long time ago and not by me that I have updated from V2 to V3 and I am trying to draw range rings from a centered LatLng point. This worked in V2 but it is not working in V3 and I can't figure out why as I know some of the code is depreciated but not sure what it needs to be replaced with.
//Function to draw circles
function doDrawCircle(circleUnits, center, circleRadius, liColor, liWidth, liOpa, fillColor, fillOpa, opts, radials){
var bounds = new google.maps.LatLngBounds();
var circlePoints = Array();
with (Math) {
if (circleUnits == 'KM') {
var d = circleRadius/6378.8; // radians
}
else { //miles
var d = circleRadius/3963.189; // radians
}
var lat1 = (PI/180)* center.lat(); // radians
var lng1 = (PI/180)* center.lng(); // radians
for (var a = 0 ; a < 361 ; a++ ) {
var tc = (PI/180)*a;
var y = asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc));
var dlng = atan2(sin(tc)*sin(d)*cos(lat1),cos(d)-sin(lat1)*sin(y));
var x = ((lng1-dlng+PI) % (2*PI)) - PI ; // MOD function
var point = new google.maps.LatLng(parseFloat(y*(180/PI)),parseFloat(x*(180/PI)));
circlePoints.push(point);
bounds.extend(point);
if(a==0){
var offset = new google.maps.Size(-5,0); // Added the offset - mile markers look a bit better
var label = new ELabel(point, circleRadius, "style1", offset, 40);
map.addOverlay(label);
}
if (((a==0) || (a==45) || (a==90) || (a==135) || (a==180) || (a==225) || (a==270) || (a==315)) && radials) {
//if (((a==0) || (a==45) || (a==90) || (a==135) || (a==180)) && radials) {
var pline = new google.maps.Polyline([center,point] , liColor, liWidth, liOpa);
map.addOverlay(pline);
}
}
var poly = new google.maps.Polygon(circlePoints, liColor, liWidth, liOpa, fillColor, fillOpa, opts);
map.addOverlay(poly); // Add a target circle to the map
map.setZoom(map.getBoundsZoomLevel(bounds)); // This sets the map bounds to be as big as the target circles, comment out if you don't want it
}
}
Then I have this within the initialize() function for the map.
// You can add circles, or change other parameters
// radials should be set to true for the maximum distance if you want radials
// doDrawCircle(circleUnits, center, circleRadius, lineColor, lineWidth, lineOpacity, fillColor, fillOpacity, opts, radials)
doDrawCircle('MI',llCenter, 62, lcolor, 1, .7, "#FFFF00", 0, { clickable: false }, false);
doDrawCircle('MI',llCenter, 124, lcolor, 1, .7, "#FFFF00", 0, { clickable: false }, false);
doDrawCircle('MI',llCenter, 187, lcolor, 1, .7, "#FFFF00", 0, { clickable: false }, false);
doDrawCircle('MI',llCenter, 249, lcolor, 1, .7, "#FFFF00", 0, { clickable: false }, false);
doDrawCircle('MI',llCenter, 312, lcolor, 1, .7, "#FFFF00", 0, { clickable: false }, false);
doDrawCircle('MI',llCenter, 374, lcolor, 1, .7, "#FFFF00", 0, { clickable: false }, false);
// doDrawCircle('MI',llCenter, 374, lcolor, 1, .7, '#00FF00', 0, { clickable: false }, true); // This would add the radials
This is what it is suppose to look like. This is from the working V2 map.
V2 EXAMPLE
Link to full code
FULL MAP CODE
First thing you'll need to do is get the newest version of elabel.js for google maps V3 here:
https://github.com/erunyon/ELabel/blob/master/elabel.js
Then, the good news is you don't need all of that complicated math and stuff which you have going on in your doDrawCircle function. You can now make use of google.maps.Circle as well as the geometry library which must be included via the google maps script tag's url parameters using a 'libraries=geometry' parameter so we can get the text label placement position starting point via google.maps.geometry.spherical.computeOffset. And then I've included a little tweeking of the text placement below to look a bit more tidy.
Test case:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Circles</title>
<style type="text/css">
.style1 {
/* used for range numbers on rings */
color: #FFF;
font-size: 10px;
text-shadow: 2px 2px 2px #000;
font-weight: bold;
font-family: verdana, helvetica, arial, sans-serif;
background-color:black;
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=geometry"></script>
<!-- elabel.js for google maps V3 from here: https://github.com/erunyon/ELabel/blob/master/elabel.js -->
<script src="elabel.js"></script>
<script>
function initialize() {
var i, meters, options, labelLocation, textLength, textXcenter, label,
//note I declared the actual font pixel size in .style1 css rule
//just to help with visualizing the way I'm positioning the label texts
textPixelSize = 10,
//will need to invert textYcenter as well as textXcenter to negative numbers later
textYcenter = (textPixelSize / 2) + 2, //2px tweak for 'y' position, approximation
mapOptions = {
zoom: 6,
center: new google.maps.LatLng(32.8297,-96.6486),
mapTypeId: google.maps.MapTypeId.HYBRID
},
ranges = [62, 124, 187, 249, 312, 374], //circle radii in miles
circles = [],
labels = [],
map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
for (i = 0; i < ranges.length; i++) {
//convert miles to meters:
meters = ranges[i] / 0.00062137;
options = {
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 1,
fillOpacity: 0,
map: map,
center: mapOptions.center,
radius: meters
};
circles.push(new google.maps.Circle(options));//ta-da! easy circles in V3
//labelLocation will be a google.maps.LatLng object
labelLocation = google.maps.geometry.spherical.computeOffset(mapOptions.center, meters, 0);
textLength = (''+ranges[i]).length;
textXcenter = (textLength * textPixelSize) / 2; //approximation
label = new ELabel({
latlng: labelLocation,
label: ranges[i],
classname: 'style1',
offset: new google.maps.Size(-textXcenter, -textYcenter),//negative will move left and up
opacity: 100,
overlap: true,
clicktarget: false
});
label.setMap(map);
labels.push(label);
}
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map_canvas" style="width:780px; height:600px; margin:10px auto;"></div>
</body>
</html>
EDIT:
Here is revised version which allows to toggle visibility of the rings via the 'Range' button. Also removed all the text label positioning adjustment math and replaced with using style classes for different length texts instead (makes it easier to just use em units in the styles if desired). Added LabelCircle constructor for easier encapsulation and simultaneous control of circles & labels.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Circles</title>
<style type="text/css">
.style1 {
/*used for range numbers on rings*/
color: #FFF;
font-size: .6em;
text-shadow: 2px 2px 2px #000;
font-weight: bold;
font-family: verdana, helvetica, arial, sans-serif;
background-color:black;
margin-top: -0.5em;
}
.d2 { /*two-digit numbers on rings*/
margin-left: -1em;
}
.d3 { /*three-digit numbers on rings*/
margin-left: -1.5em;
}
/*direct copy of your existing .button style rule*/
.button{
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
text-align: center;
position: relative;
font-family: Arial, sans-serif;
font-size: 13px;
font-weight:bold;
box-shadow: rgba(0, 0, 0, 0.4) 0 2px 4px;
-moz-box-shadow: rgba(0, 0, 0, 0.4) 0 2px 4px;
-webkit-box-shadow: rgba(0, 0, 0, 0.4) 0 2px 4px;
color: #000;
border: 1px solid #717B87;
background-color: #fff;
margin: 5px;
padding: 1px 6px;
overflow: hidden;
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=geometry"></script>
<!-- elabel.js for google maps V3 from here: https://github.com/erunyon/ELabel/blob/master/elabel.js -->
<script src="elabel.js"></script>
<script>
function LabelCircle(options) {
this.circle = new google.maps.Circle(options.circleOptions);
this.label = new ELabel(options.labelOptions);
this.label.setMap(options.circleOptions.map);
this.isVisible = true;
}
LabelCircle.prototype.setVisible = function (bool) {
var method = (bool) ? 'show' : 'hide';
this.circle.setVisible(bool);
this.label[method]();
this.isVisible = bool;
};
//a direct copy of your existing function
function buttonControl(options) {
var control = document.createElement('DIV');
control.innerHTML = options.name;
control.className = 'button';
control.index = 1;
// Add the control to the map
options.gmap.controls[options.position].push(control);
google.maps.event.addDomListener(control, 'click', options.action);
return control;
}
function initialize() {
var i, meters, options, labelLocation, textLength, label,
mapOptions = {
zoom: 6,
center: new google.maps.LatLng(32.8297,-96.6486),
mapTypeId: google.maps.MapTypeId.HYBRID
},
ranges = [62, 124, 187, 249, 312, 374], //circle radii in miles
labelCircles = [],
map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
for (i = 0; i < ranges.length; i++) {
//convert miles to meters:
meters = ranges[i] / 0.00062137;
//labelLocation will be a google.maps.LatLng object
labelLocation = google.maps.geometry.spherical.computeOffset(mapOptions.center, meters, 0);
//we'll use textLength below to add a class to the label
textLength = (''+ranges[i]).length;
options = {
circleOptions: {
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 1,
fillColor: 'transparent',
fillOpacity: 0,
map: map,
center: mapOptions.center,
radius: meters
},
labelOptions: {
latlng: labelLocation,
label: ranges[i],
classname: 'style1 d' + textLength,
//offset: //no longer needed, using style classes
opacity: 100,
overlap: true,
clicktarget: false
}
};
labelCircles.push(new LabelCircle(options));
}
var rangeOptions = {
gmap: map,
name: 'Range',
position: google.maps.ControlPosition.TOP_RIGHT,
action: function(){
for (var tmp, i = 0; i < labelCircles.length; i++) {
tmp = labelCircles[i];
tmp.setVisible(!tmp.isVisible);
}
}
};
var rangeButton = buttonControl(rangeOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map_canvas" style="width:780px; height:600px; margin:10px auto;"></div>
</body>
</html>
Related
I'm trying to add markers and a search bar to my leaflet choropleth map. However, I keep running into an error that tells me: Uncaught TypeError: this.callInitHooks is not a function. It comes from my leaflet.js file, and am unsure how to fix it. Most of my error seems to come from the leaflet links I've copied into my body before my script, or because of me trying to add a search bar or markets onto my map. I pasted my code below:
<html>
<head>
<title>How to make a choropleth map with Leaflet.js</title>
<meta charset="utf-8">
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.0/dist/leaflet.css" />
<link rel="stylesheet" href="leaflet-search.css" />
<script src="censustracts.js"></script>
<style type="text/css">
html, body, #map{
height: 100%;
padding: 0;
margin: 0;
}
.info {
padding: 6px 8px;
font: 14px/16px Arial, Helvetica, sans-serif;
background: white;
background: rgba(255,255,255,0.8);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
}
.info h4 {
margin: 0 0 5px;
color: #777;
}
.legend {
line-height: 18px;
color: #555;
}
.legend i {
width: 18px;
height: 18px;
float: left;
margin-right: 8px;
opacity: 0.7;
}
</style>
</head>
<body>
<div id="map"></map>
<script src="https://unpkg.com/leaflet#1.3.0/dist/leaflet.js"></script>
<script src="leaflet-search.js"></script>
<script type="text/javascript">
var map = L.map('map').setView([37.8, -96], 4);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {attribution: 'OSM'})
.addTo(map);
L.geoJson(statesData).addTo(map);
L.geoJson(statesData).addTo(map);
function getColor(d) {
return d > 1000 ? '#800026' :
d > 500 ? '#BD0026' :
d > 200 ? '#E31A1C' :
d > 100 ? '#FC4E2A' :
d > 50 ? '#FD8D3C' :
d > 20 ? '#FEB24C' :
d > 10 ? '#FED976' :
'#FFEDA0';
}
function style(feature) {
return {
fillColor: getColor(feature.properties.density),
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
};
}
L.geoJson(statesData, {style: style}).addTo(map);
///Functionality
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 5,
color: '#666',
dashArray: '',
fillOpacity: 0.7
});
info.update(layer.feature.properties);
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
}
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: zoomToFeature
});
}
geojson = L.geoJson(statesData, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
//Add info
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this.update();
return this._div;
};
// method that we will use to update the control based on feature properties passed
info.update = function (props) {
this._div.innerHTML = '<h4>US Population Density</h4>' + (props ?
'<b>' + props.name + '</b><br />' + props.density + ' people / mi<sup>2</sup>'
: 'Hover over a state');
};
info.addTo(map);
//Add Legend
var legend = L.control({position: 'bottomright'});
legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend'),
grades = [0, 10, 20, 50, 100, 200, 500, 1000],
labels = [];
// loop through our density intervals and generate a label with a colored square for each interval
for (var i = 0; i < grades.length; i++) {
div.innerHTML +=
'<i style="background:' + getColor(grades[i] + 1) + '"></i> ' +
grades[i] + (grades[i + 1] ? '–' + grades[i + 1] + '<br>' : '+');
}
return div;
};
legend.addTo(map);
///Bind Popups
var data = [
{"loc":[32,-86], "title":"aquamarine"},
{"loc":[41.575730,13.002411], "title":"black"},
{"loc":[41.807149,13.162994], "title":"blue"},
{"loc":[41.507149,13.172994], "title":"chocolate"},
{"loc":[41.847149,14.132994], "title":"coral"},
{"loc":[41.219190,13.062145], "title":"cyan"},
{"loc":[41.344190,13.242145], "title":"darkblue"},
{"loc":[41.679190,13.122145], "title":"Darkred"},
{"loc":[41.329190,13.192145], "title":"Darkgray"},
{"loc":[41.379290,13.122545], "title":"dodgerblue"},
{"loc":[41.409190,13.362145], "title":"gray"},
{"loc":[41.794008,12.583884], "title":"green"},
{"loc":[41.805008,12.982884], "title":"greenyellow"},
{"loc":[41.536175,13.273590], "title":"red"},
{"loc":[41.516175,13.373590], "title":"rosybrown"},
{"loc":[41.506175,13.273590], "title":"royalblue"},
{"loc":[41.836175,13.673590], "title":"salmon"},
{"loc":[41.796175,13.570590], "title":"seagreen"},
{"loc":[41.436175,13.573590], "title":"seashell"},
{"loc":[41.336175,13.973590], "title":"silver"},
{"loc":[41.236175,13.273590], "title":"skyblue"},
{"loc":[41.546175,13.473590], "title":"yellow"},
{"loc":[41.239190,13.032145], "title":"white"}
];
var markersLayer = new L.LayerGroup(); //layer contain searched elements
map.addLayer(markersLayer);
var controlSearch = L.Control.Search({
position:'topright',
layer: markersLayer,
initial: false,
zoom: 12,
marker: false
});
map.addControl( controlSearch );
for(i in data) {
var title = data[i].title, //value searched
loc = data[i].loc, //position found
marker = new L.Marker(new L.latLng(loc), {title: title} );//se property searched
marker.bindPopup('title: '+ title );
markersLayer.addLayer(marker);
}
</script>
</body>
</html>
Don't use new with lowercase function:
marker = new L.Marker(new L.latLng(loc), {title: title} );
Use new L.LatLng(loc) or L.latLng(loc) without new
I need to add a marker that moves on the points (polyline), I tried with https://docs.mapbox.com/mapbox-gl-js/example/animate-point-along-route but it's for a line, I have many points.. I don't know how to do..I tried this Animate an icon by using serious of Lat/lon Array - Mapbox js but it doesn't work.. thanks for your help !
var marker = new mapboxgl.Marker({
color: '#EC0868',
className: 'marker'
})
.setLngLat([6.161062, 45.36362])
.addTo(map);
var coords = [
[6.157903, 45.361839],
[6.15835, 45.361932],
[6.161408,45.3634445],
[6.161558, 45.363508],
[6.161307, 45.363462],
[6.1610, 45.363570],
[6.161057, 45.36362]
];
map.on('load', function () {
fitMap(map, coords);
displayJourney(map, coords);
});
function fitMap(map, coords) {
var bounds = coords.reduce(function (bounds, coord) {
return bounds.extend(coord);
}, new mapboxgl.LngLatBounds(coords[0], coords[0]));
map.fitBounds(bounds, {
padding: 30
});
}
function displayJourney(map, coords) {
map.addLayer({
"id": "journey",
"type": "line",
"source": {
"type": "geojson",
"data": {
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": coords
}
}
},
"paint": {
"line-color": "#888",
"line-width": 4,
'line-dasharray': [2, 2]
}
});
}
I combined Emma's and Moritz's code below;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Animate a point along a route</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v2.0.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.0.1/mapbox-gl.css" rel="stylesheet" />
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<style>
.overlay {
position: absolute;
top: 10px;
left: 10px;
}
.overlay button {
font: 600 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
background-color: #3386c0;
color: #fff;
display: inline-block;
margin: 0;
padding: 10px 20px;
border: none;
cursor: pointer;
border-radius: 3px;
}
.overlay button:hover {
background-color: #4ea0da;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/#turf/turf#5/turf.min.js"></script>
<div id="map"></div>
<div class="overlay">
<button id="replay">Replay</button>
</div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoieW9jaGkiLCJhIjoiY2tjZThvdWExMDV2dDJxcDgxZzBwbzlxYSJ9.M0yRA6SXDMRgXzXGuYnvsg';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [6.161062, 45.36362],
zoom: 9
});
var coords = [
[6.157903, 45.361839],
[6.15835, 45.361932],
[6.161408,45.3634445],
[6.161558, 45.363508],
[6.161307, 45.363462],
[6.1610, 45.363570],
[6.161057, 45.36362]
];
var route = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'LineString',
'coordinates': coords
}
}
]
};
var marker = new mapboxgl.Marker({
color: '#EC0868',
className: 'marker'});
// Calculate the distance in kilometers between route start/end point.
var lineDistance = turf.length(route.features[0]);
var arc = [];
// Number of steps to use in the arc and animation, more steps means
// a smoother arc and animation, but too many steps will result in a
// low frame rate
var steps = 500;
// Draw an arc between the `origin` & `destination` of the two points
for (var i = 0; i < lineDistance; i += lineDistance / steps) {
var segment = turf.along(route.features[0], i);
arc.push(segment.geometry.coordinates);
}
// Update the route with calculated arc coordinates
route.features[0].geometry.coordinates = arc;
// Used to increment the value of the point measurement against the route.
var counter = 0;
map.on('load', function () {
// Add a source and layer displaying a point which will be animated in a circle.
map.addSource('route', {
'type': 'geojson',
'data': route
});
map.addLayer({
'id': 'route',
'source': 'route',
'type': 'line',
'paint': {
'line-width': 2,
'line-color': '#007cbf'
}
});
marker.setLngLat(route.features[0].geometry.coordinates[0]).addTo(map);
fitMap(map, coords);
function animate() {
// Update point geometry to a new position based on counter denoting
// the index to access the arc
marker.setLngLat(route.features[0].geometry.coordinates[counter]);
counter = counter + 1;
// Request the next frame of animation as long as the end has not been reached
if (counter < steps) {
requestAnimationFrame(animate);
}
}
document
.getElementById('replay')
.addEventListener('click', function () {
// Set the coordinates of the original point back to origin
marker.setLngLat(route.features[0].geometry.coordinates[0])
// Reset the counter
counter = 0;
// Restart the animation
animate(counter);
});
// Start the animation
animate(counter);
});
function fitMap(map, coords) {
var bounds = coords.reduce(function (bounds, coord) {
return bounds.extend(coord);
}, new mapboxgl.LngLatBounds(coords[0], coords[0]));
map.fitBounds(bounds, {
padding: 30
});
}
https://codepen.io/OttyLab/pen/GRjBjwq
Hi Emma you can still use the approach. It also works for multiple coordinates given. Please see my example below, which does it for 3 coordinates. (1 coordinate pair extra) You can add as many additional "waypoints" as you like:
(to make it work, replace "YOUR ACCCESS TOKEN" )
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Animate a point along a route</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://api.mapbox.com/mapbox-gl-js/v2.0.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.0.1/mapbox-gl.css" rel="stylesheet" />
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<style>
.overlay {
position: absolute;
top: 10px;
left: 10px;
}
.overlay button {
font: 600 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
background-color: #3386c0;
color: #fff;
display: inline-block;
margin: 0;
padding: 10px 20px;
border: none;
cursor: pointer;
border-radius: 3px;
}
.overlay button:hover {
background-color: #4ea0da;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/#turf/turf#5/turf.min.js"></script>
<div id="map"></div>
<div class="overlay">
<button id="replay">Replay</button>
</div>
<script>
mapboxgl.accessToken = '<YOUR ACCESS TOKEN>';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: [-96, 37.8],
zoom: 3
});
// San Francisco
var origin = [-122.414, 37.776];
var waypoint1 = [-100.032, 38.913];
// Washington DC
var destination = [-77.032, 38.913];
// A simple line from origin to destination.
var route = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'LineString',
'coordinates': [origin, waypoint1, destination] // here you can add the additional waypoints
}
}
]
};
// A single point that animates along the route.
// Coordinates are initially set to origin.
var point = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'properties': {},
'geometry': {
'type': 'Point',
'coordinates': origin
}
}
]
};
// Calculate the distance in kilometers between route start/end point.
var lineDistance = turf.length(route.features[0]);
var arc = [];
// Number of steps to use in the arc and animation, more steps means
// a smoother arc and animation, but too many steps will result in a
// low frame rate
var steps = 500;
// Draw an arc between the `origin` & `destination` of the two points
for (var i = 0; i < lineDistance; i += lineDistance / steps) {
var segment = turf.along(route.features[0], i);
arc.push(segment.geometry.coordinates);
}
// Update the route with calculated arc coordinates
route.features[0].geometry.coordinates = arc;
// Used to increment the value of the point measurement against the route.
var counter = 0;
map.on('load', function () {
// Add a source and layer displaying a point which will be animated in a circle.
map.addSource('route', {
'type': 'geojson',
'data': route
});
map.addSource('point', {
'type': 'geojson',
'data': point
});
map.addLayer({
'id': 'route',
'source': 'route',
'type': 'line',
'paint': {
'line-width': 2,
'line-color': '#007cbf'
}
});
map.addLayer({
'id': 'point',
'source': 'point',
'type': 'symbol',
'layout': {
'icon-image': 'airport-15',
'icon-rotate': ['get', 'bearing'],
'icon-rotation-alignment': 'map',
'icon-allow-overlap': true,
'icon-ignore-placement': true
}
});
function animate() {
var start =
route.features[0].geometry.coordinates[
counter >= steps ? counter - 1 : counter
];
var end =
route.features[0].geometry.coordinates[
counter >= steps ? counter : counter + 1
];
if (!start || !end) return;
// Update point geometry to a new position based on counter denoting
// the index to access the arc
point.features[0].geometry.coordinates =
route.features[0].geometry.coordinates[counter];
// Calculate the bearing to ensure the icon is rotated to match the route arc
// The bearing is calculated between the current point and the next point, except
// at the end of the arc, which uses the previous point and the current point
point.features[0].properties.bearing = turf.bearing(
turf.point(start),
turf.point(end)
);
// Update the source with this new data
map.getSource('point').setData(point);
// Request the next frame of animation as long as the end has not been reached
if (counter < steps) {
requestAnimationFrame(animate);
}
counter = counter + 1;
}
document
.getElementById('replay')
.addEventListener('click', function () {
// Set the coordinates of the original point back to origin
point.features[0].geometry.coordinates = origin;
// Update the source layer
map.getSource('point').setData(point);
// Reset the counter
counter = 0;
// Restart the animation
animate(counter);
});
// Start the animation
animate(counter);
});
</script>
</body>
</html>
Of course you can also create a list of coordinates, and pass this list to the geoJson route object.
I am using this webgl earth for the project, but I can't seem to find a way to change the position of the planet on the canvas. My desired goal was to put the planet on the bottom of the page, but I can't find a way around it. I am providing the simple working demo for the issue. Thank you in advance.
function initialize() {
var options = {
// sky: true,
atmosphere: true,
zooming: false,
};
var earth = new WE.map('earth_div', options);
WE.tileLayer('https://webglearth.github.io/webglearth2-offline/{z}/{x}/{y}.jpg', {
tileSize: 256,
tms: true
}).addTo(earth);
// Start a simple rotation animation
var before = null;
requestAnimationFrame(function animate(now) {
var c = earth.getPosition();
var elapsed = before ? now - before : 0;
before = now;
earth.setCenter([c[0], c[1] + 0.1 * (elapsed / 30)]);
requestAnimationFrame(animate);
});
var marker = WE.marker([51.5, -0.09]).addTo(earth);
marker.bindPopup("<b>Hello world!</b><br>I am a popup.<br /><span style='font-size:10px;color:#999'>Tip: Another popup is hidden in Cairo..</span>", {
maxWidth: 150,
closeButton: true
}).openPopup();
var marker2 = WE.marker([30.058056, 31.228889]).addTo(earth);
marker2.bindPopup("<b>Cairo</b><br>Yay, you found me!", {
maxWidth: 120,
closeButton: false
});
var markerCustom = WE.marker([50, -9], '/img/logo-webglearth-white-100.png', 100, 24).addTo(earth);
// earth.setView([51.505, 0], 2.5);
}
html,
body {
padding: 0;
margin: 0;
background-color: white;
font-family: 'MyWebFont';
overflow: hidden;
}
#earth_div {
top: 0;
right: 0;
bottom: 0;
left: 0;
position: absolute !important;
}
<script src="http://www.webglearth.com/v2/api.js"></script>
<body onload="initialize()">
<div id="earth_div">
</div>
The last line of your code. Sets camera to the given latitude / longitude position. Leaflet's LatLng is supported. ie:
earth.setView([51.505, 0], 6);
I'm trying to create an online map using OpenLayers 3.
I'm brand new to using OpenLayers, and all I'm trying to do is to get the coordinates of the points, lines, polygons that I drew on the map. I'm aware that there is a featuresadded parameter available, but I'm not able to implement it correctly.
Can anybody point me in the right direction how to get the coordinates of a drawn feature (either in an alert() or console.log)?
Thanks a ton!!
here's my code:
<html>
<head>
<script src="http://openlayers.org/en/v3.3.0/build/ol.js" type="text/javascript"></script>
<link rel="stylesheet" href="ol.css" type="text/css">
<style type="text/css">
body {
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
font-size: small;
}
#map {
clear: both;
position: relative;
border: 1px solid black;
}
#wrapper {
width: 337px;
height: 50px;
}
#location {
float: right;
font-family: Arial, Verdana, sans-serif;
font-size: 12px;
color: #483D8B;
background-color: white;
}
#nodelist{
font-family: Arial, Verdana, sans-serif;
font-size: 14px;
color: #000000;
font-style: normal;
background-color: white;
}
</style>
<script type="text/javascript">
var map;
var draw;
var transformed_coordinate;
var vector;
function init() {
var view = new ol.View({
center: ol.proj.transform([13.1929, 55.718],'EPSG:4326', 'EPSG:3857'),
zoom: 12
});
var myZoom = new ol.control.Zoom();
var myZoomSlider = new ol.control.ZoomSlider();
var zoomToExtentControl = new ol.control.ZoomToExtent({
extent: [1453297.22,7490484.81,1483872.03,7513415.91]
});
var myScaleLine = new ol.control.ScaleLine()
var neighborhood = new ol.source.ImageWMS({
url: 'http://localhost:8090/geoserver/project/wms',
params: {'LAYERS': 'project:Neighborhood'}
});
var roads = new ol.source.ImageWMS({
url: 'http://localhost:8090/geoserver/project/wms',
params: {'LAYERS': 'project:Roads_all'}
});
var source = new ol.source.Vector({wrapX: false});
vector = new ol.layer.Vector({
source: source,
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(0, 255, 0, 0.5)'
}),
stroke: new ol.style.Stroke({
color: '#ffcc33',
width: 2
}),
image: new ol.style.Circle({
radius: 7,
fill: new ol.style.Fill({
color: '#ffcc33'
})
})
})
});
var layers = [
new ol.layer.Image({
source: neighborhood
}),
new ol.layer.Image({
source: roads
}),
vector
]
map = new ol.Map({
layers: layers,
target: 'map',
view: view
});
map.addControl(myZoom);
map.addControl(myZoomSlider);
map.addControl(zoomToExtentControl);
map.addControl(myScaleLine);
map.on('singleclick', function(evt){
var coord = evt.coordinate;
transformed_coordinate = ol.proj.transform(coord, "EPSG:3857", "EPSG:4326");
//console.log(transformed_coordinate);
})
function onFeaturesAdded(event){
var bounds = event.features[0].geometry.getBounds();
var answer = "bottom: " + bounds.bottom + "\n";
answer += "left: " + bounds.left + "\n";
answer += "right: " + bounds.right + "\n";
answer += "top: " + bounds.top + "\n";
alert(answer);
}
var typeSelect = document.getElementById('type');
function addInteraction() {
var value = typeSelect.value;
if (value !== 'None') {
var geometryFunction, maxPoints;
if (value === 'Square') {
value = 'Circle';
geometryFunction = ol.interaction.Draw.createRegularPolygon(4);
} else if (value === 'Box') {
value = 'LineString';
maxPoints = 2;
geometryFunction = function(coordinates, geometry) {
if (!geometry) {
geometry = new ol.geom.Polygon(null);
}
var start = coordinates[0];
var end = coordinates[1];
geometry.setCoordinates([
[start, [start[0], end[1]], end, [end[0], start[1]], start]
]);
return geometry;
};
}
draw = new ol.interaction.Draw({
source: source,
type: /** #type {ol.geom.GeometryType} */ (value),
geometryFunction: geometryFunction,
maxPoints: maxPoints
});
map.addInteraction(draw);
}
}
/**
* Let user change the geometry type.
* #param {Event} e Change event.
*/
typeSelect.onchange = function(e) {
map.removeInteraction(draw);
addInteraction();
};
addInteraction();
} //end init
</script>
</head>
<body onload="init()">
<div id="map" style="width: 800px; height: 600px"></div>
<form class="form-inline">
<label>Geometry type </label>
<select id="type">
<option value="None">None</option>
<option value="Point">Point</option>
<option value="LineString">LineString</option>
<option value="Polygon">Polygon</option>
</select>
</form>
</body>
</html>
Register a listener on ol.source.Vector and wait until the drawn feature is added:
source.on('addfeature', function(evt){
var feature = evt.feature;
var coords = feature.getGeometry().getCoordinates();
});
Use drawend event
drawend (ol.interaction.DrawEvent) - Triggered upon feature draw ends
Ex:
this.draw = new ol.interaction.Draw();
this.draw.on('drawend', function(evt){
//in evt you will get ol.feature
// from ol.feature get the geometry and than get coordinates
});
Let me know if I am wrong.
So, my question is - I have a small map on my page and I want open this map in a full screen on the same page by pressing a button and close it in the same way. I think it can be done by removing div styles when you press the button but I didn't succeed, may be this is a wrong way? I understand that is a simple question but my google skills aren't well developed and I just don't know how to ask this question properly, so I'm asking here.
My code is simply generated by GoogleMapsTileCutter plus I've added some code from here for limit panning. This is all code that I have done.
<!DOCTYPE html>
<html lang="en">
<head>
<title>PS_Bramus.GoogleMapsTileCutter</title>
<meta charset="utf-8" />
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
width:100%;
height:100%;
color: #CCC;
background: #EFEFEF;
}
span.loading {
display: block;
text-align: center;
font: 300 italic 72px/400px "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", sans-serif;
}
</style>
</head>
<body>
<div id="map"><span class="loading">loading tiles...</span></div>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script>
/*
* = PS_Bramus.GoogleMapsTileCutter Config
* ----------------
*/
var repeatOnXAxis = false; // Do we need to repeat the image on the X-axis? Most likely you'll want to set this to false
/*
* Helper function which normalizes the coords so that tiles can repeat across the X-axis (horizontally) like the standard Google map tiles.
* ----------------
*/
function getNormalizedCoord(coord, zoom) {
if (!repeatOnXAxis) return coord;
var y = coord.y;
var x = coord.x;
// tile range in one direction range is dependent on zoom level
// 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
var tileRange = 1 << zoom;
// don't repeat across Y-axis (vertically)
if (y < 0 || y >= tileRange) {
return null;
}
// repeat across X-axis
if (x < 0 || x >= tileRange) {
x = (x % tileRange + tileRange) % tileRange;
}
return {
x: x,
y: y
};
}
/*
* Main Core
* ----------------
*/
window.onload = function() {
// Define our custom map type
var customMapType = new google.maps.ImageMapType({
getTileUrl: function(coord, zoom) {
var normalizedCoord = getNormalizedCoord(coord, zoom);
if(normalizedCoord && (normalizedCoord.x < Math.pow(2, zoom)) && (normalizedCoord.x > -1) && (normalizedCoord.y < Math.pow(2, zoom)) && (normalizedCoord.y > -1)) {
return zoom + '_' + normalizedCoord.x + '_' + normalizedCoord.y + '.jpg';
} else {
return 'empty.jpg';
}
},
tileSize: new google.maps.Size(256, 256),
maxZoom: 5,
name: 'PS_Bramus.GoogleMapsTileCutter'
});
// Basic options for our map
var myOptions = {
center: new google.maps.LatLng(0, 0),
zoom: 3,
minZoom: 3,
disableDefaultUI: true,
streetViewControl: false,
mapTypeControl: false,
mapTypeControlOptions: {
mapTypeIds: ["custom"]
}
};
// Init the map and hook our custom map type to it
var map = new google.maps.Map(document.getElementById('map'), myOptions);
map.mapTypes.set('custom', customMapType);
map.setMapTypeId('custom');
// bounds of the desired area
var allowedBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-47,-95.61),
new google.maps.LatLng(47,95.61)
);
var lastValidCenter = map.getCenter();
google.maps.event.addListener(map, 'center_changed', function() {
if (allowedBounds.contains(map.getCenter())) {
// still within valid bounds, so save the last valid position
lastValidCenter = map.getCenter();
return;
}
// not valid anymore => return to last valid position
map.panTo(lastValidCenter);
});
}
</script>
</body>
If it's a matter of simply resizing your map div, then the following JavaScript should lead you in the right direction:
function resizeMap() {
var myMap = document.getElementById('map-canvas');
myMap.style.height = 100%;
myMap.style.width = 100%;
}
Note that the percentages are only relative to ancestor elements of your map div.
Then you'd map your buttons onclick to the resizeMap() function and you're set!
If you wanted a jQuery approach, this is what the above code could look like:
function resizeMap() {
$("#map-canvas").css({height: 100%, width: 100%});
}
To revert the map back to its original width and height, you could first check if the width and height are set to 100%, then resize, but a more elegant solution would be to use a class instead of inline styles, which could resemble the following:
function resizeMap() {
var myMap = document.getElementById('map-canvas');
myMap.classList.toggle("fullscreen");
}
Then in your css you can have:
.fullscreen {
width: 100%;
height: 100%;
}
Clicking the button would just toggle between turning the class on and off.
Like this, the answers above do not work.
var mapOptions = {
center: myCenter,
zoom: 9,
fullscreenControl: true
};