I need to create new geofence circles dynamically for each location I get from a Worklight Adapter. I need to do it using a loop, as I dunno how many locations are defined in backend service. Furthermore a new location can be define meanwhile the application is running.
I have seen several samples for an established set of locations but I dunno how to get it working in my scenario...
I have already found a solution. This is the code in case someone needs it:
var triggers = new Object();
triggers.Geo={};
triggers.Geo.Cliente = {
type: "Enter",
circle: {
longitude: xxxxxx,
latitude: xxxxxxx,
radius: proximidad // 300m
},
confidenceLevel: "high", // ~95% confidence that we are in the circle
eventToTransmit: {
event: {
name: 'clientecerca'
},
transmitImmediately: true
}
};
Generating Triggers using an external function:
var triggers = new Object();
triggers.Geo={};
triggers.Geo.Cliente = generaTriggers("41.43373","-3.80052");
function generaTriggers(lat,lon){
var Cliente ={
type: "Enter",
circle: {
longitude: lon,
latitude: lat,
radius: proximidad // 300m
},
confidenceLevel: "high", // ~95% confidence that we are in the circle
eventToTransmit: {
event: {
name: 'clientecerca'
},
transmitImmediately: true
}
};
return Cliente;
}
This is the final result for adding dynamically location triggers:
*Note: vector is a javascript array that contains the latitude and longitude of the different geofences circles*
function Geofencing(){
var policy = { Geo: WL.Device.Geo.Profiles.LiveTracking() };
var triggers = new Object();
triggers.Geo={};
var triggersgenerados = generaTriggers();
triggers.Geo = triggersgenerados;
WL.Device.startAcquisition(policy, triggers, geoFailure);
WL.App.setKeepAliveInBackground(true);
}
function generaTriggers(){
var triggersvisitas= new Object;
for(var i=0; i< vector.length;i++){
var Cliente ={
type: "Enter",
circle: {
longitude: vector[i].longitud,
latitude: vector[i].latitud,
radius: proximidad // 300m
},
confidenceLevel: "high",
eventToTransmit: {
event: {
name: 'clientecerca'
},
transmitImmediately: true
}
};
triggersvisitas["Cliente"+i]=Cliente;
}
return triggersvisitas;
}
Related
I have two leaflet maps which are loaded with geojson on the basis of a search button click. This shows some polygons with a style opacity 0.3 so you can see the street names under the polygons.
It works great except that any additional searches and loading of polygons starts to change the opacity of the polygon, making it more solid so you cant read the names of the streets under the polygon.
I try clearing the geojson layer before adding to the map, but the issue persists.
I have created a rough code pen of the issue here:
https://codepen.io/joomkit/pen/xxXgLPJ?editors=1111
Essentially just click the search button to load the layer no need to fill the listener runs a function and gets static data.
I have tried various methods to remove layer. A second click on the search is meant to clear the layer and load a new one. In the example it's just reloading the original data but the opacity is clearly demonstrated.
Main code is also below.
var geoMap2;
var lamap = new L.Map("map2", {
center: new L.LatLng(51.44094723464765, 0.048892332250943187),
// center: new L.LatLng(39.75621,-104.99404),
zoom: 14,
maxZoom: 18
});
var osm2 = L.tileLayer(
"https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
{
attribution:
'© OpenStreetMap contributors © CARTO',
subdomains: "abcd",
maxZoom: 18
}
);
lamap.addLayer(osm2);
searchButton.addEventListener("click", function (e) {
let searchQuery = inputSearch.value;
// searchOpenFunding(searchQuery);
setLaMap(data);
});
function setLaMap(data) {
removeLayers();
let geojsonFeatureCollection2 = {
type: "FeatureCollection",
features: setFeatureCollection2(data)
};
geoMap2 = L.geoJSON(geojsonFeatureCollection2, {
style: polyStyleLAMap,
onEachFeature: function myonEachFeatureLaMap(feature, layer) {
layer.myTag = "myGeoJSON";
}
}).addTo(lamap);
lamap.setMaxBounds(lamap.fitBounds(geoMap2.getBounds()));
lamap.setZoom(13);
}
var removeLayers = function () {
lamap.eachLayer(function (layer) {
if (layer.myTag && layer.myTag === "myGeoJSON") {
lamap.removeLayer(layer);
console.log("rem layer from ");
}
});
};
function setFeatureCollection2(data) {
for (const [key, item] of Object.entries(data)) {
// setup lealfet geojson collection from data mapit api is incomplete pe ritem so we build it here
geoJsonFeatures2.push({
type: "Feature",
properties: {
craftentryId: item.id,
areaId: item.mapitAreaId,
lsoacode: item.lsoacode,
localauthority: item.localauthority,
openForFunding: item.openForFunding,
fundableRegion: item.fundableRegion,
title: item.title,
popupContent: ""
},
geometry: item.geojson
});
}
return geoJsonFeatures2;
}
function polyStyleLAMap(feature) {
return {
fillColor: getFillColorLaMap(feature.properties.openForFunding),
weight: 1,
opacity: 1,
color: getBorderColor(feature.properties.openForFunding),
dashArray: "0",
fillOpacity: 0.3
};
}
function getFillColorLaMap(d) {
return d === true ? "#FFFFFF" : d === false ? "#FED976" : "#FED976";
}
function getBorderColor(d) {
return d === true ? "#0e9c12" : d === false ? "#adabab" : "#cccccc";
}
Look here https://codepen.io
The code I modified is:
// 029A, 029C
function setLaMap(data) {
removeLayers();
let geojsonFeatureCollection2 = {
type: "FeatureCollection",
features: setFeatureCollection2(data),
};
console.log(geojsonFeatureCollection2);
const geoMap2 = L.geoJSON(geojsonFeatureCollection2, {
style: polyStyleLAMap,
onEachFeature: function (feature, layer) {
layer.myTag = "myGeoJSON";
},
});
map.addLayer(geoMap2);
map.setMaxBounds(map.fitBounds(geoMap2.getBounds()));
// map.setZoom(13);
}
function removeLayers() {
map.eachLayer((layer) => {
if (layer.myTag && layer.myTag === "myGeoJSON") {
console.log(layer);
map.removeLayer(layer);
}
});
}
You need to keep the geojson layer in a global variable and remove it before over writing with the new layer data.
var geoMap2;
function setLaMap(data){
if(geoMap2) { // if geoMap2 is set remove it
lamap.removeLayer(geoMap2)
}
const geojsonFeatureCollection2 = {
"type": "FeatureCollection",
"features": setFeatureCollection2(data)
}
geoMap2 = L.geoJSON(geojsonFeatureCollection2, {
style: polyStyleLAMap,
onEachFeature: myonEachFeatureLaMap
})
.........
I am using Leaflet Map with geocoder (ESRI) and Routing Machine.
I have added two markers, let's say my home and my work
var marker_work = L.marker([50.27, 19.03], { title: 'MyWork'}).addTo(map)
.bindPopup("work").openPopup();
var marker_home = L.marker([50.10, 18.4], { title: 'MyHome'}).addTo(map)
.bindPopup("home").openPopup();
Here is an example fiddle:
https://jsfiddle.net/21nmk8so/1/
How can I add this markers/point as a predefined places for ControlGeocoder?
I want to use them in search and use as a start point / end point for route calculation.
Another example for the same question: how to add custom-fake city with lat/lon and be able to search (find route) to/from that city.
I don't know if this is the best solution but it is working:
Create a custom Geocoder Class which overwrites the geocode function. There you can overwrite the result function and apply suggestions to the result.
L.CustomGeocoder = L.Control.Geocoder.Nominatim.extend({
suggestions: [],
setSuggestions(arr){
this.suggestions = arr;
},
createSuggestionFromMarker(marker){
this.suggestions.push({name: marker.options.title, center: marker.getLatLng()});
},
getResultsOfSuggestions(query){
var results = [];
this.suggestions.forEach((point)=>{
if(point.name.indexOf(query) > -1){
point.center = L.latLng(point.center);
point.bbox = point.center.toBounds(100);
results.push(point);
}
});
return results;
},
geocode(query, resultFnc, context) {
var that = this;
var callback = function(results){
var sugg = that.getResultsOfSuggestions(query);
resultFnc.call(this,sugg.concat(results));
}
L.Control.Geocoder.Nominatim.prototype.geocode.call(that,query, callback, context);
}
})
Then you have to use the new Geocoder Class:
var geocoder = new L.CustomGeocoder({});
var control = L.Routing.control({
waypoints: [],
router: new L.Routing.osrmv1({
language: 'en',
profile: 'car'
}),
geocoder: geocoder
}).addTo(map);
And finally you can add suggestions over markers and theier title option over createSuggestionFromMarker(marker) or setSuggestions(arr):
var suggestions = [
{
name: 'Test Car 1',
center: [50.27, 19.03]
},
{
name: 'Test Car 2',
center: [50.10, 18.4]
}
];
geocoder.setSuggestions(suggestions);
var marker_work = L.marker([50.27, 19.03], { title: 'MyWork'}).addTo(map);
var marker_home = L.marker([50.10, 18.4], { title: 'MyHome'}).addTo(map);
geocoder.createSuggestionFromMarker(marker_work);
geocoder.createSuggestionFromMarker(marker_home);
Update, use marker Ref instead of fix latlng
Change this two function, then the marker is referenced and it always searches from the current position of the marker:
createSuggestionFromMarker(marker){
this.suggestions.push({name: marker.options.title, marker: marker});
},
getResultsOfSuggestions(query){
var results = [];
this.suggestions.forEach((point)=>{
if(point.name.indexOf(query) > -1){
if(point.marker){
point.center = point.marker.getLatLng();
}
point.center = L.latLng(point.center);
point.bbox = point.center.toBounds(100);
results.push(point);
}
});
return results;
},
You can test this in the demo, when you drag the marker
https://jsfiddle.net/falkedesign/hu25jfd1/
I need to take the visitor's location when clicking a ajax-button and only then send the form data to the action.
So far I am able to conditionally submit based on a class existence.
The problem is that the navigator.geolocation condition in my code doesn't get parsed despite the agree popup appearing to user and the OK being clicked.
It just skips this and display the fallback location.
Here is my changed default sails ajax-button component:
methods: {
click: async function () {
let btnDelayed = document.getElementById('btn-search');
if (btnDelayed && btnDelayed.classList.contains('delayed')) {
console.log('btn-search cannot emit click');
await this.loadGeoData();
} else {
console.log('btn-search can emit click ');
this.$emit('click');
}
},
loadGeoData: async function () {
let loc = await this.getLocation();
console.log('location');
console.dir(loc);
/*
let test = {
lat: 0.22,
lng: 0.55
};
*/
let btnSubmit = document.getElementById('btn-search');
btnSubmit.classList.remove('delayed');
let pos = JSON.stringify(loc);
this.$emit('pos', pos);
console.log('pos event value ' + pos);
this.click();
},
getLocation: async function () {
let loc = {
lat: 0,
lng: 0
};
if (navigator.geolocation) {
let options = {
enableHighAccuracy: true,
timeout: 10000,
maximumAge: 0
};
console.log('geolocation loaded');
navigator.geolocation.getCurrentPosition(function (position) {
console.log('getting position...');
console.dir(position);
loc.lat = position.coords.latitude;
loc.lng = position.coords.longitude;
}, (err) => {
console.warn(`ERROR(${err.code}): ${err.message}`);
}, options);
}
return loc;
}
}
And here is the console log output:
btn-search cannot emit click
geolocation loaded
location
{…}
lat: 0
lng: 0
<prototype>: Object { … }
pos event value {"lat":0,"lng":0}
btn-search can emit click
// getting the data in the instance
argins
{…}
__ob__: Object { value: {…}, dep: {…}, vmCount: 0 }
actionref: "{\"lat\":0,\"lng\":0}"
actiontype: "word"
search:
The avigator.geolocation.getCurrentPosition gets though executed but adter the form is submit:
getting position... ajax-button.component.js:108:13
Position
coords: Coordinates { latitude: 44.00000000000, longitude: 26.00000000000, accuracy: 931, … }
timestamp: 1548163982134
_
Of course, I need it executed before.
For further readers,
After days of searching I found this post, Catch Geolocation Error - Async Await, explaining how to make geolocation work with async/await.
The rest was just a matter of modifying the ajax-button component click event.
// inital Locations
var myLocations = [{
name: "Istanbul",
address: "214 S Highland Ave, Pittsburgh, PA",
latlng: {
lat: 41.008238,
lng: 28.978359
}
}, {
name: "Antalya",
address: "5469 Penn Ave Pittsburgh, PA 15206",
latlng: {
lat: 36.896891,
lng: 30.713323
}
}, {
name: "Ankara",
address: "236 Fifth Ave Pittsburgh, PA 15222",
latlng: {
lat: 39.933363,
lng: 32.859742
}
}, {
name: "Trabzon",
address: "5608 Walnut St Pittsburgh, PA 15232",
latlng: {
lat: 41.002697,
lng: 39.716763
}
}, {
name: "Bursa",
address: "5841 Penn Ave Pittsburgh, PA 15206",
latlng: {
lat: 40.188528,
lng:29.060964
}
},
];
//String to display in info window
//Declare Map variable and markers array
var map;
var infoWindow;
var marker;
//Create Instance of a map from the Google maps api
//Grab the reference to the "map" id to display map
//Set the map options object properties
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
center: {
lat: 38.963745,
lng: 35.243322
},
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP,
mapTypeControl: true,
mapTypeControlOptions: {
style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
}
});
};
// tells the view model what to do when a change occurs
function mLocation(value) {
this.name = ko.observable(value.name);
this.address = ko.observable(value.address);
this.description = ko.observable(value.description);
this.latlng = ko.observable(value.lat);
};
//ViewModel
function ViewModel() {
var self = this;
self.markers = [];
//Copies the values of initialLocations and stores them in sortedLocations(); observableArray
self.sortedLocations = ko.observableArray(myLocations);
//Adds new markers at each location in the initialLocations Array
self.sortedLocations().forEach(function(location) {
marker = new google.maps.Marker({
position: location.latlng,
map: map,
title: location.name,
icon: 'img/marker.png',
animation: google.maps.Animation.DROP
});
location.marker = marker;
var content = '<div id="iw_container">' +
'<div class="iw_title">' + name + </div>';
//Pushes each marker into the markers array
this.markers.push(marker);
});
//Map info windows to each item in the markers array
self.markers.map(function(info) {
infoWindow = new google.maps.InfoWindow({
content: content
});
//Add click event to each marker to open info window
info.addListener('click', function() {
infoWindow.open(map, this),
info.setAnimation(google.maps.Animation.BOUNCE) //Markers will bounce when clicked
setTimeout(function() {
info.setAnimation(null)
}, 2000); //Change value to null after 2 seconds and stop markers from bouncing
});
});
//Click on item in list view
self.listViewClick = function(loc) {
if (loc.name) {
map.setZoom(15); //Zoom map view
map.panTo(loc.latlng); // Pan to correct marker when list view item is clicked
loc.marker.setAnimation(google.maps.Animation.BOUNCE); // Bounce marker when list view item is clicked
infoWindow.open(map, loc.marker); // Open info window on correct marker when list item is clicked
}
setTimeout(function() {
loc.marker.setAnimation(null); // End animation on marker after 2 seconds
}, 2000);
};
// Stores user input
self.query = ko.observable('');
//Filter through observableArray and filter results using knockouts utils.arrayFilter();
self.search = ko.computed(function() {
return ko.utils.arrayFilter(self.sortedLocations(), function(listResult) {
return listResult.name.toLowerCase().indexOf(self.query().toLowerCase()) >= 0;
});
});
};
$(document).ready(function() {
initMap();
ko.applyBindings(ViewModel());
});
I would like to ask about how to add an info window to a marker in Google Maps. The condition is, I have to create a program with multiple markers on a map. But how I can give a specified info window to each marker when select in each place in the view list?
I'm trying to add markers to a map using the
L.mapbox.featureLayer({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ll[0], ll[1]]
},
properties: {
title: d[i].screen_name,
media: d[i].media_url,
id: d[i].source_id,
text: d[i].text,
userId: d[i].user_id,
'marker-color': '#000',
'marker-symbol': 'star-stroked'
}
}).addTo(map);
method but how do I from there access that marker? map.getLayerAt(0) or something?
It doesn't; display on the map for some reason..
Mocked this up quickly based on an example on the Mapbox site:
var map = L.mapbox.map('map', 'examples.map-zr0njcqy');
map.featureLayer.on('ready', function(e) {
var markers = [];
this.eachLayer(function(marker) { markers.push(marker); });
cycle(markers);
});
function cycle(markers) {
var i = 0;
function run() {
if (++i > markers.length - 1) i = 0;
var marker = markers[i];
console.log(marker.getLatLng());
}
run();
}
You can use the cycle() function to do stuff with each individual marker - if you look in the console you'll see that you can know access the marker's internal properties like latLng etc. I don't know what you want to do with each marker, so if you provide more information I'll be able to assist you more!