Drawing a polygon on user's location leaflet - geolocation
I'm currently building a web app for a project and was wondering if anybody could help me. I'm basically making a GPS game where the whole map is covered in fog and to remove the fog user's have to visit each location and login.
For the fog I'm just going to use a giant polygon to cover the entire map and then using the user's location, generate a polygon at the user's current location, cut that polygon from the fog polygon and then finally re-apply it.
Could anybody advise me as to how I could add a hexagon shaped polygon at the users location.
I'm currently getting the user's location using the geolocation API but I'm also a little stumped as to draw the hexagon. Below I've got the location code which drops a marker on the user location and a basic static polygon(polygon is a placeholder nothing more).
I've tried playing the navigator code to add a polygon at the users location but it keeps breaking the map and I'm a bit stumped as to how to do it.
Any help would be greatly appreciated.
navigator.geolocation.getCurrentPosition(function (position) {
var userMarker = L.marker([position.coords.latitude, position.coords.longitude]).addTo(mymap);
})
var userPolygon = [{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-1.5268367528915405,54.98091997292441],
[-1.5270352363586424,54.98074450562538],
[-1.5267670154571533,54.98054748879672],
[-1.5262091159820557,54.980541332005245],
[-1.5259945392608643,54.98072911372045],
[-1.5262305736541748,54.98091689455734],
[-1.5268367528915405,54.98091997292441]
]
]
}
}].addTo(mymap);
Thank you #FalkeDesign for your help thus far. I've added the updated code below and would really appreciate if you could point out why they aren't showing
<script>
var mymap = L.map('mapid').setView([57.149860, -2.102930], 13);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(mymap);
// //var to capture user position and place marker
// navigator.geolocation.getCurrentPosition(function (position) {
// var userMarker = L.marker([position.coords.latitude, position.coords.longitude]).addTo(mymap);
// })
var fog = L.rectangle([[54.98652836571122, -1.542469837492442], [54.973032422136875, -1.4963848081781708]]);
var fogLayer = L.geoJSON(fog.toGeoJSON()).addTo(myMap);
function subtract() {
var radius = 0.01;
var hex = createHex(myMap.getCenter(), radius); //replace center with navigator point
var templayer = L.geoJSON(turf.difference(fogLayer.getLayers()[0].toGeoJSON(), hex.toGeoJSON()));
fogLayer.removeFrom(myMap);
fogLayer = templayer.addTo(myMap)
}
function createHex(center, radius) {
var centerp = turf.point([center.lng, center.lat]);
var points = [];
points.push(turf.destination(centerp, radius, 90));
points.push(turf.destination(centerp, radius, 150));
points.push(turf.destination(centerp, radius, -150));
points.push(turf.destination(centerp, radius, -90));
points.push(turf.destination(centerp, radius, -30));
points.push(turf.destination(centerp, radius, 30));
var hex = L.polygon([]);
points.forEach((p) => {
hex.addLatLng([p.geometry.coordinates[1], p.geometry.coordinates[0]]);
});
return hex;
}
subtract();
myMap.on('move', subtract);
////Add custom user icon
// var userIcon = L.icon({
// iconUrl: 'prototype\img\icons8-user-location-48.png',
// iconSize: [48, 48], // size of the icon
// shadowSize: [0, 0], // size of the shadow
// iconAnchor: [22, 94], // point of the icon which will correspond to marker's location
// });
//Fog polygon
var fogPolygon =
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-175.078125,
84.9901001802348
],
[
-174.375,
-85.05112877980659
],
[
197.578125,
-84.92832092949963
],
[
197.578125,
84.9901001802348
],
[
-175.078125,
84.9901001802348
]
]
]
}
}
]
};
L.geoJSON(fogPolygon, {
style: function (feature) {
return {
color: '#000000',
weight: 0.2,
}
}
}).addTo(mymap);
/*PoI Placeholders*/
var duthiePark = L.polygon([
[57.12896, -2.107036],
[57.12882, -2.101028],
[57.130765, -2.097294],
[57.132512, -2.102809],
[57.131906, -2.107852],
[57.130776, -2.106779]
]).addTo(mymap);
var robertGordon = L.polygon([
[57.119828, -2.150552],
[57.117568, -2.148342],
[57.117661, -2.136669],
[57.118232, -2.129974],
[57.11963, -2.130404],
[57.119886, -2.136497],
[57.118768, -2.14493]
]).addTo(mymap);
var pittodrie = L.polygon([
[57.159646, -2.090192],
[57.158436, -2.090299],
[57.158587, -2.086523],
[57.159693, -2.086844],
]).addTo(mymap);
var unionTerraceGardens = L.polygon([
[57.14575, -2.101521],
[57.147612, -2.103775],
[57.147612, -2.104461],
[57.147147, -2.104826],
[57.145598, -2.102122]
]).addTo(mymap);
var unionSquare = L.polygon([
[57.144166, -2.098882],
[57.145098, -2.095771],
[57.142653, -2.092767],
[57.141582, -2.095771]
]).addTo(mymap);
var marischalCollege = L.polygon([
[57.150301, -2.096007],
[57.149475, -2.097552],
[57.14838, -2.096329],
[57.149044, -2.095428],
[57.14951, -2.095084],
[57.150232, -2.095504],
]).addTo(mymap);
var robertGordonCollege = L.polygon([
[57.147822, -2.1032177],
[57.147868, -2.100663],
[57.148858, -2.101521],
[57.149638, -2.103066],
[57.149381, -2.104053]
]).addTo(mymap);
var robertGordonCollege = L.polygon([
[57.147822, -2.1032177],
[57.147868, -2.100663],
[57.148858, -2.101521],
[57.149638, -2.103066],
[57.149381, -2.104053]
]).addTo(mymap);
var aberdeenBeach = L.polygon([
[57.142428, -2.068863],
[57.145315, -2.073669],
[57.153602, -2.078819],
[57.164122, -2.079248],
[57.173149, -2.077446],
[57.158536, -2.078047],
[57.147829, -2.074614],
[57.1428, -2.06809]
]).addTo(mymap);
var victoriaPark = L.polygon([
[57.15256, -2.124707],
[57.150599, -2.123762],
[57.151966, -2.118838],
[57.152781, -2.119181]
]).addTo(mymap);
var westburnPark = L.polygon([
[57.155969, -2.126412],
[57.152664, -2.124674],
[57.152845, -2.121574],
[57.153648, -2.121187],
[57.153625, -2.119911],
[57.154934, -2.120233]
]).addTo(mymap);
var aberdeenHarbour = L.polygon([
[57.14536, -2.095031],
[57.145803, -2.093743],
[57.146263, -2.087681],
[57.144526, -2.075472],
[57.142011, -2.069764],
[57.140498, -2.075686],
[57.140498, -2.089033]
]).addTo(mymap);
var aberdeenUniversity = L.polygon([
[57.166762, -2.106467],
[57.167129, -2.10224],
[57.166721, -2.097305],
[57.16575, -2.097702],
[57.164498, -2.097402],
[57.164557, -2.099805],
[57.163067, -2.099869],
[57.163266, -2.102047],
[57.161973, -2.101736],
[57.162404, -2.10429],
[57.16337, -2.107208],
[57.165404, -2.106156],
]).addTo(mymap);
var hazleheadPark = L.polygon([
[57.142869, -2.17643],
[57.139067, -2.172997],
[57.137635, -2.177374],
[57.137013, -2.180786],
[57.139591, -2.183726],
[57.140953, -2.176816],
[57.142502, -2.178082]
]).addTo(mymap);
/*Display lat/long coordinates - Development use only! */
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent(e.latlng.toString())
.openOn(mymap);
}
mymap.on('click', onMapClick);
/*Popups*/
robertGordon.bindPopup("Robert Gordon University");
pittodrie.bindPopup("Pittodrie Stadium");
duthiePark.bindPopup("Duthie Park");
unionTerraceGardens.bindPopup("Union Terrace Gardens")
marischalCollege.bindPopup("Marischal College and Dancing Water Fountains")
robertGordonCollege.bindPopup("Robert Gordon College and Art Gallery")
aberdeenBeach.bindPopup("Aberdeen Beach")
unionSquare.bindPopup("Union Square")
victoriaPark.bindPopup("Victoria park")
westburnPark.bindPopup("Westburn Park")
hazleheadPark.bindPopup("Hazlehead Park")
aberdeenUniversity.bindPopup("Aberdeen University")
aberdeenHarbour.bindPopup("Aberdeen harbour")
.addTo(mymap);
</script>
This is working with the turfjs library:
<script src="https://cdn.jsdelivr.net/npm/#turf/turf#5.1.6/turf.js"></script>
Creating hex shape:
var hex = createHex(myMap.getCenter(),radius); //replace center with navigator point
function createHex(center,radius) {
var centerp = turf.point([center.lng, center.lat]);
var points = [];
points.push(turf.destination(centerp, radius, 90));
points.push(turf.destination(centerp, radius, 150));
points.push(turf.destination(centerp, radius, -150));
points.push(turf.destination(centerp, radius, -90));
points.push(turf.destination(centerp, radius, -30));
points.push(turf.destination(centerp, radius, 30));
var hex = L.polygon([]);
points.forEach((p)=>{
hex.addLatLng([p.geometry.coordinates[1],p.geometry.coordinates[0]]);
});
return hex;
}
To remove the hex from the "fog":
var fog = L.rectangle([[54.98652836571122, -1.542469837492442],[ 54.973032422136875, -1.4963848081781708]]);
var fogLayer = L.geoJSON(fog.toGeoJSON()).addTo(myMap);
function subtract() {
var radius = 0.01;
var hex = createHex(myMap.getCenter(),radius); //replace center with navigator point
var templayer = L.geoJSON(turf.difference(fogLayer.getLayers()[0].toGeoJSON(), hex.toGeoJSON()));
fogLayer.removeFrom(myMap);
fogLayer = templayer.addTo(myMap)
}
Example: https://jsfiddle.net/falkedesign/6a4smLgt/
I hope you don't forget to check the answer ;)
Related
Capturing touch events (touchstart and touchmove) in addition to mousemove for synchronising highcharts
I have been building a basic webpage which displays data from a weather station with multiple synchronised highcharts, with the help of others here and here I have been able to implement a fully working version for mouse based systems (windows etc), how do I adapt the code below to also capture the touchstart and touchmove events: //catch mousemove event and have all charts' crosshairs move along indicated values on x axis function syncronizeCrossHairs(chart) { var container = $(chart.container), offset = container.offset(), x; container.mousemove(function(evt) { x = evt.clientX - chart.plotLeft - offset.left; //remove old plot line and draw new plot line (crosshair) for this chart var xAxis1 = chart1.xAxis[0], points = [], points1 = [], points2 = [], points3 = [], points4 = [], e = chart1.pointer.normalize(evt); // Find coordinates within the chart chart1.series.forEach(s => { var point = s.searchPoint(e, true) if (point) { point.setState(); points.push(point) } }) if (points) { var number = 0; Highcharts.each(points, function(p, i) { if (!p.series.visible) { points.splice(i - number, 1); number++; } }) if (points.length) { chart1.tooltip.refresh(points); // Show the tooltip } } xAxis1.removePlotLine("myPlotLineId"); xAxis1.addPlotLine({ value: chart.xAxis[0].translate(x, true), width: 1, id: "myPlotLineId" }); /*----- second chart ------*/ var xAxis2 = chart2.xAxis[0]; chart2.series.forEach(s => { var point = s.searchPoint(e, true) if (point) { point.setState(); points1.push(point) } }) if (points1[0]) { var number = 0; Highcharts.each(points1, function(p, i) { if (!p.series.visible) { points1.splice(i - number, 1); number++; } }) if (points1.length) { chart2.tooltip.refresh(points1); // Show the tooltip } } xAxis2.removePlotLine("myPlotLineId"); xAxis2.addPlotLine({ value: chart.xAxis[0].translate(x, true), width: 1, id: "myPlotLineId" }); /*----- third chart ------*/ var xAxis3 = chart3.xAxis[0]; chart3.series.forEach(s => { var point = s.searchPoint(e, true) if (point) { point.setState(); points2.push(point) } }) if (points2[0]) { var number = 0; Highcharts.each(points1, function(p, i) { if (!p.series.visible) { points2.splice(i - number, 1); number++; } }) if (points2.length) { chart3.tooltip.refresh(points2); // Show the tooltip } } xAxis3.removePlotLine("myPlotLineId"); xAxis3.addPlotLine({ value: chart.xAxis[0].translate(x, true), width: 1, id: "myPlotLineId" }); //if you have other charts that need to be syncronized - update their crosshair (plot line) in the same way in this function. }); } Thanks
Base on this example from official Highcharts demo base https://www.highcharts.com/demo/synchronized-charts I was able to wrap similar pattern to your code. Demo: http://jsfiddle.net/BlackLabel/mnLzbe1s/ ['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) { var container = $(chart.container), offset = container.offset(), x; container[0].addEventListener(eventType, (function(evt) { x = evt.clientX - chart.plotLeft - offset.left; //remove old plot line and draw new plot line (crosshair) for this chart var xAxis1 = chart1.xAxis[0], points = [], points1 = [], points2 = [], points3 = [], e = chart1.pointer.normalize(evt); // Find coordinates within the chart ... }) ) })
Drag and Drop limitation in Konva js
I recently began to learn Konva-JS... please help me :) <script> var width = window.innerWidth; var height = window.innerHeight; function loadImages(sources, callback) { var assetDir = '/assets/'; var images = {}; var loadedImages = 0; var numImages = 0; for(var src in sources) { numImages++; } for(var src in sources) { images[src] = new Image(); images[src].onload = function() { if(++loadedImages >= numImages) { callback(images); } }; images[src].src = assetDir + sources[src]; } } function isNearOutline(animal, outline) { var a = animal; var o = outline; var ax = a.getX(); var ay = a.getY(); if(ax > o.x - 20 && ax < o.x + 20 && ay > o.y - 20 && ay < o.y + 20) { return true; } else { return false; } } function drawBackground(background, beachImg, text) { var context = background.getContext(); context.drawImage(beachImg, 0, 0); context.setAttr('font', '20pt Calibri'); context.setAttr('textAlign', 'center'); context.setAttr('fillStyle', 'white'); context.fillText(text, background.getStage().getWidth() / 2, 40); } function initStage(images) { var stage = new Konva.Stage({ container: 'container', width: 578, height: 530 }); var background = new Konva.Layer(); var animalLayer = new Konva.Layer(); var animalShapes = []; var score = 0; // image positions var animals = { snake: { x: 10, y: 70 }, giraffe: { x: 90, y: 70 }, monkey: { x: 275, y: 70 }, lion: { x: 400, y: 70 } }; var outlines = { snake_black: { x: 275, y: 350 }, giraffe_black: { x: 390, y: 250 }, monkey_black: { x: 300, y: 420 }, lion_black: { x: 100, y: 390 } }; // create draggable animals for(var key in animals) { // anonymous function to induce scope (function() { var privKey = key; var anim = animals[key]; var animal = new Konva.Image({ image: images[key], x: anim.x, y: anim.y, draggable: true }); animal.on('dragstart', function() { this.moveToTop(); animalLayer.draw(); }); /* * check if animal is in the right spot and * snap into place if it is */ animal.on('dragend', function() { var outline = outlines[privKey + '_black']; if(!animal.inRightPlace && isNearOutline(animal, outline)) { animal.position({ x : outline.x, y : outline.y }); animalLayer.draw(); animal.inRightPlace = true; if(++score >= 4) { var text = 'You win! Enjoy your booty!'; drawBackground(background, images.beach, text); } // disable drag and drop setTimeout(function() { animal.draggable(false); }, 50); } }); // make animal glow on mouseover animal.on('mouseover', function() { animal.image(images[privKey + '_glow']); animalLayer.draw(); document.body.style.cursor = 'pointer'; }); // return animal on mouseout animal.on('mouseout', function() { animal.image(images[privKey]); animalLayer.draw(); document.body.style.cursor = 'default'; }); animal.on('dragmove', function() { document.body.style.cursor = 'pointer'; }); animalLayer.add(animal); animalShapes.push(animal); })(); } // create animal outlines for(var key in outlines) { // anonymous function to induce scope (function() { var imageObj = images[key]; var out = outlines[key]; var outline = new Konva.Image({ image: imageObj, x: out.x, y: out.y }); animalLayer.add(outline); })(); } stage.add(background); stage.add(animalLayer); drawBackground(background, images.beach, 'Ahoy! Put the animals on the beach!'); } var sources = { beach: 'beach.png', snake: 'snake.png', snake_glow: 'snake-glow.png', snake_black: 'snake-black.png', lion: 'lion.png', lion_glow: 'lion-glow.png', lion_black: 'lion-black.png', monkey: 'monkey.png', monkey_glow: 'monkey-glow.png', monkey_black: 'monkey-black.png', giraffe: 'giraffe.png', giraffe_glow: 'giraffe-glow.png', giraffe_black: 'giraffe-black.png' }; loadImages(sources, initStage); </script> as we can see in this example Animals_on_the_Beach_Game the animal's images are drag-able and can be drop ever where.... but I want to change it in the way that it just can drop on the specific place ... what can I do ? thank you :)
This is more of a design question, as letting go of the mouse button isn't something you can prevent. It would also be non-intuitive to keep the image attached to the mouse position as you would then need a new mouse event to associate with dropping it. What I've done for a drag and drop UI was to either (1) destroy the dropped shape, or if that wasn't an option, (2) animate the shape back (i.e. snap back) to its original position. Alternatively, you might (3) find the closest likely valid drop target and snap to that location.
First you define lionOrigin, that maybe you already have. You have to implement the call on the dragend event of the object dragged, so let's say the lion. You have to check position of the lion in relation to the end desired position, let's call it lionDestiny. That can be done with a simple grometry: calculate the distance between to point. We do that with distanceA2B() function. Now you can establish an offset inside wich you can snap the object, as it is close enough. If the minimal offset is not achieved, then you place the lion back on lionOrigin. Al last, in konvajs you can use .x() and .y() to easily get or set position to lion. Something like this: var lionOrigin = [50,50]; var lionDestiny = [200,200]; var offset = 20; distanceA2B(a,b) { return Math.sqrt( ((a[0]-b[0])*(a[0]-b[0])) + ((a[1]-b[1])*(a[1]-b[1])) ); } lion.on('dragend', (e) => { var d = distanceA2B([lion.x(),lion.y()],lionDestiny); if(d<offset){ lion.x(lionDestiny[0]); lion.y(lionDestiny[1]); }else{ lion.x(lionOrigin[0]); lion.y(lionOrigin[1]); } }); Hope this helps!
It would have been better if you could explain your question more when you say you want to move any shape to a specific position. Though konva.js provides you with various events through which you can do this. For example, suppose you want to interchange the location of two shapes when you drag and move the first shape to the second and drop it there. In this case, you can use dragend event of konva. So when you move the target element to another element and drop it there, check if they are intersecting each other or not and then interchange their coordinates with each other. Here is the function to find the intersection between two elements: haveIntersection(r1, r2) { return !( r2.x > r1.x + r1.width || r2.x + r2.width < r1.x || r2.y > r1.y + r1.height || r2.y + r2.height < r1.y ); } And from here, you can try to understand the functionality. Though it's in nuxt.js but the events and scripts would be almost same if you are using only javascript. You can find sample code with an explanation for replacing the location of two shapes with each other. So even if you don't want to replace the locations but you want to move your target element to any position this will make you understand how to do this.
Draw a circle again after vector export
After draw a circle in my map, I exported it with: getAsJson : function() { var geojson = new ol.format.GeoJSON(); var features = this.vectorSource.getFeatures(); var jsonData = geojson.writeFeatures( features,{ featureProjection: ol.proj.get('EPSG:3857'), dataProjection: ol.proj.get('EPSG:4326') }); return jsonData; } and the result was: {"type":"FeatureCollection","features":[ {"type":"Feature","geometry":{ "type":"GeometryCollection","geometries":[] },"properties":{ "circleCenter":[-4805776.093508227,-2600749.7153150304],"circleRadius":6658.801529937424 } }]} This is how I take the circle center and radius: var draw = new ol.interaction.Draw({ source: vectorSource, type: value, // Can be Circle,Point,Line,Polygon // No Geometry Function when type is 'Circle' (omited code to simplify) geometryFunction: geometryFunction, maxPoints: maxPoints }); draw.on('drawend', function( evt ){ var geometry = evt.feature.getGeometry(); // Check the type before try to get this! (omited code to simplify) var center = geometry.getCenter(); var radius = geometry.getRadius(); evt.feature.set('circleCenter', center ); evt.feature.set('circleRadius', radius ); }); map.addInteraction( draw ); Now I'm trying to use this JSON to draw the same circle again, but it have no geometry and this is not working (work for all other geometries like point, polygong and line so the problem is it not the code): var features = new ol.format.GeoJSON().readFeatures( jsonData, { featureProjection: 'EPSG:3857' }); var vectorSource = new ol.source.Vector({ }); var vectorLayer = new ol.layer.Vector({ source: vectorSource, style : customStyleFunction }); map.addLayer( vectorLayer ); Just a note: I can see now the projection of center and radius was not changed. Must work on this too...
GeoJSON does not support Circle Geometry. So ol.format.GeoJSON() format doesnot convert JSON to ol.Feature object. So write a custom method which consumes the JSON data and creates a Circle geometry var featuresJson = geoJson.features; for ( var i=0; i<featuresJson.length; i++ ) { var radius = featuresJson[i].properties.circleRadius; var center = featuresJson[i].properties.circleCenter; var feature = new ol.Feature(new ol.geom.Circle(center,radius); vectorSource.addFeature(feature); }
I think this can help me somehow... will see. map.getViewport().addEventListener("dblclick", function(e) { var coordinate = map.getEventCoordinate(e); vectorSource.addFeature(new ol.Feature(new ol.geom.Circle(coordinate, dist))); });
i want to develop a dynamic url lake google max , when i send ajax request?
in search/index.html <div id="map"></div> in search.js var lat = 17.0123; var lng = 28.0124538; var position = new google.maps.LatLng(lat,lng); var zoomLevel = 8; var mapId = document.getElementById('map'); var map; function initMap(){ map = new google.maps.Map(mapId, { center: position, zoom: zoomLevel, mapTypeId: google.maps.MapTypeId.ROADMAP }); } google.maps.event.addListener(map, 'idle', function() { var map_centre = map.getCenter(); lat = map_centre.lat(); lng = map_centre.lng(); } });//idel function end Now i want to get the dynamic url of zoom level and latitude and langitude of map using turbo links in my rails project. Can you help me how to do?
How to animate a line string between 2 points in OpenLayers 3 map?
I want to draw a line between multiple points from an array of coordinates. My code looks like : <button onclick="drawAnimatedLine(new ol.geom.Point(6210355.674114,2592743.9994331785), new ol.geom.Point(8176927.537835015,2255198.08252584), 50, 2000);">Draw Line</button> And my js looks like : function drawAnimatedLine(startPt, endPt, steps, time, fn) { var style = { strokeColor: "#0500bd", strokeWidth: 15, strokeOpacity: 0.5, strokeColor: '#0000ff' }; var directionX = (endPt.x - startPt.x) / steps; var directionY = (endPt.y - startPt.y) / steps; var i = 0; var prevLayer; var lineDraw = setInterval(function () { console.log("Inside Animate Line"); if (i > steps) { clearInterval(lineDraw); if (fn) fn(); return; } var newEndPt = new ol.geom.Point(startPt.x + i * directionX, startPt.y + i * directionY); var line = new ol.geom.LineString([startPt, newEndPt]); var fea = new ol.Feature({ geometry:line, style: style }); var vec = new ol.layer.Vector(); vec.addFeatures([fea]); map.addLayer(vec); if(prevLayer) { map.removeLayer(prevLayer); } prevLayer = vec; i++; }, time / steps); } Note : Coordinates will be dynamic but for testing I've passed the sample data in onclick of the button. Please do try to sort out this issue as soon as possible.