I am using a leaflet Directive to show maps on openStreet map.To list all markers my code looks like this :
$scope.goTo = function() {
restAPI.getSomething().success(function(data) {
var anArray = data.lists;
console.log(anArray);
var anotherList = {};
angular.forEach(anArray, function(value, key) {
if (value.geolocation) {
$scope.project = value;
anotherList[anArray[key].ao] = {
lat: value.lat,
lng: value.lng,
focus: false,
draggable: false,
icon: icon,
message: '<button type="button" ng-click="openProject(project)">' + value.ao + '</button> {{project.ao}}',
clickable: true
}
}
});
$scope.map.anotherList = anotherList;
});
}
Here in that message template i am adding a button with a function call, As i understood to pass a variable in template, Scope is the solution, but here, on all markers value.ao name is different but project.ao is same ...(why ????)
when i click on each marker its opening last project in array(anArray).
How can i bind scope value in each iteration ?,So i can open correct project on each marker
my working solution looks like:
$scope.goTo = function() {
restAPI.getSomething().success(function(data) {
var anArray = data.lists;
console.log(anArray);
var anotherList = {};
$scope.project = {};
angular.forEach(anArray, function(value, key) {
if (value.geolocation) {
$scope.project[key] = value;
anotherList[anArray[key].ao] = {
lat: value.lat,
lng: value.lng,
focus: false,
draggable: false,
icon: icon,
message: '<button type="button" ng-click="openProject('+key+')">' + value.ao + '</button> {{project.ao}}',
clickable: true
}
}
});
$scope.map.anotherList = anotherList;
});
}
$scope.openProject = function(key){
var project = $scope.project[key];
// this is how i got correct project
}
Thanks to Edwin for pointing out to pass key value.
Related
I am making a vue project and I want to use leaflet inside of my components. I have the map showing but I run into an error when I try to add a marker to the map. I get
Uncaught TypeError: events.forEach is not a function
at VueComponent.addEvents (VM2537 Map.vue:35)
at e.boundFn (VM2533 vue.esm.js:191)
at HTMLAnchorElement. (leaflet.contextmenu.js:328)
at HTMLAnchorElement.r (leaflet.js:5)
<template>
<div>
<div id="map" class="map" style="height: 781px;"></div>
</div>
</template>
<script>
export default {
data() {
return {
map: [],
markers: null
};
},
computed: {
events() {
return this.$store.state.events;
}
},
watch: {
events(val) {
this.removeEvents();
this.addEvents(val);
}
},
methods: {
addEvents(events) {
const map = this.map;
const markers = L.markerClusterGroup();
const store = this.$store;
events.forEach(event => {
let marker = L.marker(e.latlng, { draggable: true })
.on("click", el => {
store.commit("locationsMap_center", e.latlng);
})
//.bindPopup(`<b> ${event.id} </b> ${event.name}`)
.addTo(this.map);
markers.addLayer(marker);
});
map.addLayer(markers);
this.markers = markers;
},
removeEvent() {
this.map.removeLayer(this.markers);
this.markers = null;
}
},
mounted() {
const map = L.map("map", {
contextmenu: true,
contextmenuWidth: 140,
contextmenuItems: [
{
text: "Add Event Here",
callback: this.addEvents
}
]
}).setView([0, 0], 1);
L.tileLayer("/static/map/{z}/{x}/{y}.png", {
maxZoom: 4,
minZoom: 3,
continuousWorld: false,
noWrap: true,
crs: L.CRS.Simple
}).addTo(map);
this.map = map;
}
};
</script>
New2Dis,
Here is your example running in a jsfiddle.
computed: {
events: function () {
return this.store.events;
}
},
watch: {
events: function (val) {
this.removeEvents();
this.addEvents(val);
}
},
methods: {
addEvents(events) {
console.log("hoi")
const map = this.map;
const markers = L.markerClusterGroup();
const store = this.$store;
events.forEach(event => {
let marker = L.marker(event.latlng, { draggable: true })
.on("click", el => {
//store.commit("locationsMap_center", event.latlng);
})
.bindPopup(`<b> ${event.id} </b> ${event.name}`)
.addTo(this.map);
markers.addLayer(marker);
});
map.addLayer(markers);
this.markers = markers;
},
removeEvents() {
if (this.markers != null) {
this.map.removeLayer(this.markers);
this.markers = null;
}
}
},
I did replace some things to make it works, like the $store as I don't have it, and removeEvent was not written correctly, so I'm not sure what I actually fixed...
I have also created a plugin to make it easy to use Leaflet with Vue.
You can find it here
You will also find a plugin for Cluster group here
Give it a try and let me know what you think.
I can’t access the values lat , lng from data() in maps() method.
my vue.js component
code link : https://gist.github.com/melvin2016/c8082e27b9c50964dcc742ecff853080
console image of lat,lng
enter image description here
<script>
import Vue from 'vue';
import navbarSec from './navbarSec.vue';
export default {
data(){
return{
lat: '',
lng: '',
mapState: window.mapState,
from:'',
to:'',
placesFrom:[],
placesTo:[]
};
},
components:{
'navbar':navbarSec
},
created(){
var token = this.$auth.getToken();
this.$http.post('http://localhost:3000/book',{},{headers: {'auth':token}}).then(function(data){
this.session = true;
})
.catch(function(data){
this.session = false;
this.$auth.destroyToken();
Materialize.toast(data.body.message, 6000,'rounded');
this.$router.push('/login');
});
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition((data)=>{
this.lat = data.coords.latitude;
this.lng = data.coords.longitude;
this.from=data.coords.latitude+' , '+data.coords.longitude;
});
}else{
Materialize.toast("Cannot Get Your Current Location !", 6000,'rounded');
}
},
mounted(){
if (this.mapState.initMap) {// map is already ready
var val = this.mapState.initMap;
console.log(val);
this.maps();
}
},
watch: {
// we watch the state for changes in case the map was not ready when this
// component is first rendered
// the watch will trigger when `initMap` will turn from `false` to `true`
'mapState.initMap'(value){
if(value){
this.maps();
}
},
from : function(val){
if(val){
var autoComplete = new google.maps.places.AutocompleteService();
autoComplete.getPlacePredictions({input:this.from},data=>{
this.placesFrom=data;
});
}
},
to:function(val){
if(val){
var autoComplete = new google.maps.places.AutocompleteService();
autoComplete.getPlacePredictions({input:this.to},data=>{
this.placesTo=data;
});
}
}
},
methods:{
maps(){
var vm = this;
var lati = vm.lat;
var lngi = vm.lng;
console.log(lati+' '+lngi);
var map;
var latlng = {lat: lati, lng:lngi };
console.log(latlng);
this.$nextTick(function(){
console.log('tickkkk');
map = new google.maps.Map(document.getElementById('maplo'), {
zoom: 15,
center: latlng
});
var marker = new google.maps.Marker({
position: latlng,
map: map
});
});
}
}
}
</script>
This is happening because you're calling maps() in the mounted at which point, the navigator.geolocation.getCurrentPosition((data) => {}) code hasn't resolved. With that in mind, call this.maps() within the getCurrentPosition method i.e:
navigator.geolocation.getCurrentPosition((data)=>{
this.lat = data.coords.latitude;
this.lng = data.coords.longitude;
this.from=data.coords.latitude+' , '+data.coords.longitude;
this.maps()
});
I've not looked in detail but you might be able to change the bits within the maps() method to remove the nextTick stuff when you do this as you'll be calling it a lot later in the cycle at which point everything will have been rendered.
I need to use a "resetMap" function which I defined in my initMap function (callback function for google map), to bind to reset button. Now it cannot be used because its not there in the global scope.
If I move this function to global scope its items such as mapOptions.center setzoom etc will be undefined.
This is my script file
var map;
/* Hardcoding 5 airport locations - our data - model*/
var airports = [
{
title: "Calicut International Airport",
lat: 11.13691,
lng: 75.95098,
streetAddress: "Karipur",
cityAddress: "Malappuram, Kerala",
visible: ko.observable(true),
id: "nav0",
showIt: true
},
{
title: "Chennai International Airport",
lat: 12.9920434,
lng: 80.1631409,
streetAddress: "Meenambakkam",
cityAddress: "Chennai, Tamil Nadu",
visible: ko.observable(true),
id: "nav1",
showIt: true
},
{
title: "Trivandrum International Airport",
lat: 8.4829722,
lng: 76.909139,
streetAddress: "Vallakkadavu",
cityAddress: "Thiruvananthapuram, Kerala",
visible: ko.observable(true),
id: "nav2",
showIt: true
},
{
title: "Cochin International Airport",
lat: 10.15178,
lng: 76.39296,
streetAddress: "Nedumbassery",
cityAddress: "Kochi, Kerala",
visible: ko.observable(true),
id: "nav3",
showIt: true
},
{
title: "Kempegowda International Airport",
lat: 13.2143948,
lng: 77.6896124,
streetAddress: "Devanahalli",
cityAddress: "Bengaluru, Karnataka",
visible: ko.observable(true),
id: "nav4",
showIt: true
}
];
/* Initializing map, markers */
function initMap() {
var myLatlng = new google.maps.LatLng(13.2143948, 77.6896124);
var mapOptions = {
zoom: 6,
disableDefaultUI: true
};
var bounds = new google.maps.LatLngBounds(
new google.maps.LatLng(8.4829722, 76.909139), //SW coordinates here
new google.maps.LatLng(13.2143948, 77.6896124) //NE coordinates here
);
map = new google.maps.Map(document.getElementById("map"), mapOptions);
map.fitBounds(bounds);
setMarkers(airports);
setMapWithMarker();
/* Function to reset the map zoom and set center */
function resetMap() {
map.setCenter(mapOptions.center);
map.setZoom(6);
}
$(window).resize(function(){
map.setCenter(mapOptions.center);
});
}
/* Controlling the visibility of marker based on the 'showIt' property */
function setMapWithMarker() {
for (var i = 0; i < airports.length; i++) {
if(airports[i].showIt === true) {
airports[i].locMarker.setMap(map);
} else {
airports[i].locMarker.setMap(null);
}
}
}
/* Setting markers on map and attaching content to each of their info windows */
function setMarkers(location) {
var img = 'img/airport.png';
for (var i = 0; i < location.length; i++) {
location[i].locMarker = new google.maps.Marker({
position: new google.maps.LatLng(location[i].lat, location[i].lng),
map: map,
animation: google.maps.Animation.DROP,
title: location.title,
icon:img
});
var airportTitle = location[i].title;
var wikiUrl = 'https://en.wikipedia.org/w/api.php?action=opensearch&search=' +
airportTitle + '&format=json&callback=wikiCallback';
(function(i){
var wikiRequestTimeout = setTimeout(function() {
$('.show-error').html('ERROR: Failed to load wikipedia data - Airport details will not show up! Sorry for the inconvenience caused.');
}, 5000);
$.ajax({
url: wikiUrl,
dataType: "jsonp"
}).done(function(response){
var article = response[2][0];
location[i].contentString =
'<strong>'+ location[i].title + '</strong><br><p>' + location[i].streetAddress
+ '<br>' + location[i].cityAddress + '<br></p><p>' + article +
'</p><p>Source: Wikipedia</p>';
clearTimeout(wikiRequestTimeout);
});
})(i);
/* info window initialization and setting content to each marker's info window */
var infowindow = new google.maps.InfoWindow({});
new google.maps.event.addListener(location[i].locMarker, 'click',
(function(airport, i) { return function() {
airport.setAnimation(google.maps.Animation.BOUNCE);
setTimeout(function() {
airport.setAnimation(null);
}, 2400);
infowindow.setContent(location[i].contentString);
infowindow.open(map,this);
map.setZoom(15);
map.setCenter(airport.getPosition());
};
})(location[i].locMarker, i));
/* info window call when clicked on airport menu item */
var searchNav = $('#nav' + i);
searchNav.click((function(airport, i) {
return function() {
airport.setAnimation(google.maps.Animation.BOUNCE);
setTimeout(function() {
airport.setAnimation(null);
}, 2200);
infowindow.setContent(location[i].contentString);
infowindow.open(map,airport);
map.setZoom(15);
map.setCenter(airport.getPosition());
};
})(location[i].locMarker, i));
}
}
/* Function for toggling the menu */
function slideToggle() {
$(this).toggleClass('toggled');
$( "#listing" ).toggle( "slow", function() {
// Animation complete.
});
}
/* Our view model */
function viewModel() {
var self = this;
this.locMarkerSearch = ko.observable('');
ko.computed(function() {
var search = self.locMarkerSearch().toLowerCase();
return ko.utils.arrayFilter(airports, function(airport) {
if (airport.title.toLowerCase().indexOf(search) >= 0) {
airport.showIt = true;
return airport.visible(true);
} else {
airport.showIt = false;
setMapWithMarker();
return airport.visible(false);
}
});
});
};
// Activates knockout.js
ko.applyBindings(new viewModel());
I need to bind the function here in my index.html
<footer>
<button id="reset" data-bind="click: resetMap">Reset Zoom to center</button>
</footer>
<script src="js/lib/knockout-3.4.0.js"></script>
<script src="js/script.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDVOVW9WT7QaVlFYDkE7K2Qm-AvSS02YrM&callback=initMap" async defer onerror="googleError()"></script>
How can I resolve this problem? Thanks in advance..
You haven't shown us how viewModel uses initMap, but fundamentally, initMap needs to make resetMap available to the outside world. One way to do that is to return it:
function initMap() {
// ...
function resetMap() {
}
// ...
return resetMap;
}
and then have code in viewModel put that on the view model:
function viewModel() {
this.resetMap = initMap(); // I assume you're calling this indirectly; whatever
}
Then resetMap has access to what it needs, and it's on the view model so it can be bound.
I want to achieve something similar to this http://jsfiddle.net/sya8gn0w/1/.
Now the problem is I have my own custom directive to display map. I want a function in child controller which will achieve above mentioned functionality on some button click.
eg. The place marker functionality triggers only if I click on some button.
Present code -
function ParentCtrl($scope){
var mainCtrl = this;
angular.extend(mainCtrl , {
map: {
center: {
latitude: 18.5,
longitude: 73.85
},
zoom: 13,
markers: [],
events: {
click: function (map, eventName, originalEventArgs) {
var e = originalEventArgs[0];
var lat = e.latLng.lat(), lon = e.latLng.lng();
var marker = {
id: Date.now(),
coords: {
latitude: lat,
longitude: lon
}
};
mainCtrl .map.markers.pop();
mainCtrl .map.markers.push(marker);
console.log(mainCtrl .map.markers);
console.log("latitude : "+lat+" longitude : "+lon);
$scope.$apply();
}
}
}
});
};
I want to move the functionality present in 'click:' part of angular.extend to a function presents in ChildCtrl controller. Is it possible ?
Ortherwise suggest me different approach to achieve this.
I don't know whether my answer going to help you or not.
Recently I worked on GoogleMap, I want to share what I know.
angular.extend(scope, {
map: {
show: true,
control: {},
version: 'uknown',
center: {
latitude: 0,
longitude: 0
},
options: {
streetViewControl: false,
panControl: false,
maxZoom: 10,
minZoom: 1
},
zoom: 2,
dragging: false,
bounds: {},
markers: scope.markers,
doClusterMarkers: true,
currentClusterType: 'standard',
clusterTypes: clusterTypes,
selectedClusterTypes: selectedClusterTypes,
clusterOptions: selectedClusterTypes['standard'],
events: {
tilesloaded: function(map, eventName, originalEventArgs) {},
click: function(mapModel, eventName, originalEventArgs) {},
dragend: function() {}
},
toggleColor: function(color) {
if (color === 'red') {
return '#6060FB';
} else {
return 'red';
}
}
}
Here the statement markers: scope.markers will help to create new markers or to remove old markers.
In "ChildCtrl" you write a function to update scope.markers object of parent controller.
You could declare the function for adding a marker in a separate controller
and then utilize $controller service to inject that controller into another controller:
angular.module('main', ['uiGmapgoogle-maps'])
.controller('MapCtrl', function ($scope,$controller) {
$controller('MarkerCtrl', {$scope: $scope});
angular.extend($scope, {
map: {
center: {
latitude: 42.3349940452867,
longitude:-71.0353168884369
},
zoom: 11,
markers: [],
events: {
click: function (map, eventName, originalEventArgs) {
var e = originalEventArgs[0];
$scope.addMarker(e.latLng);
$scope.$apply();
}
}
}
});
})
.controller('MarkerCtrl', function($scope) {
angular.extend($scope, {
addMarker: function(latLng) {
var marker = {
id: Date.now(),
coords: {
latitude: latLng.lat(),
longitude: latLng.lng()
}
};
$scope.map.markers.push(marker);
}
});
});
Plunker
I used different but better approach to achieve the desired results.
Found this solution on fiddle . Hope this would be helpful for someone else also.
function dragMarkerService(resolveAddressService){
this.placeMarker = function(location, idValue, map){
var myMarker = new google.maps.Marker({
position: new google.maps.LatLng(location.latitude, location.longitude),
draggable: true
});
google.maps.event.addListener(myMarker, 'dragend', function (evt) {
location.latitude = evt.latLng.lat();
location.longitude = evt.latLng.lng();
resolveAddressService.resolveAddress(location, idValue)
});
google.maps.event.addListener(myMarker, 'dragstart', function (evt) {
angular.element(idValue).val() = 'Currently dragging marker...';
});
map.setCenter(myMarker.position);
myMarker.setMap(map);
}
};
I am trying to display a pop up window in an angular leaflet map using prune cluster. However, I am getting an error that leafletView is an unknown provider. I have followed the examples on this page but i have been unsuccessful - https://github.com/SINTEF-9012/PruneCluster. Here is my code:
angular.module('bizvizmap').controller('controller', [
'$scope', '$http', '$filter', 'leafletData', 'leafletView',
function ($scope, $http, $filter, leafletData, leafletView){
$scope.center = {
lat: 39.5500507,
lng: -105.7820674,
zoom: 4
},
$scope.defaults = {
scrollWheelZoom: true
},
$scope.events = {
map: {
enable: ['zoomstart', 'drag', 'click', 'mousemove'],
logic: 'emit'
}
},
$scope.layers = {
baselayers: {
osm: {
name: 'OpenStreetMap',
url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
type: 'xyz'
}
},
markers:{}
},
$scope.map = null;
leafletData.getMap("bizvizmap").then(function(map){
$scope.map = map;
});
function renderMarkers(data, map){
var markerLayer = new PruneClusterForLeaflet();
for (var i=0; i < data.length; i++){
var marker = new PruneCluster.Marker(data[i].geometry.coordinates[1], data[i].geometry.coordinates[0]);
popup: "Marker";
markerLayer.RegisterMarker(marker);
}
map.addLayer(markerLayer);
markerLayer.ProcessView();
}
//Display PopUp
leafletView.PrepareLeafletMarker = function (marker, data) {
if (marker.getPopup()) {
marker.setPopupContent(data.company);
} else {
marker.bindPopup(data.company);
}
};
$scope.geojson = {};
$http.get('data/bizvizmap.geojson').success(function (data){
$scope.data = data;
// Render clustered markers
renderMarkers(data.features, $scope.map);
});
// Filtering markers
$scope.search = {
'NAICS':'',
'SIC':''
};
$scope.$watch('search', function(newVal, oldVal){
if(!angular.equals(newVal, oldVal)) {
var geojson = angular.copy($scope.data);
angular.forEach(newVal, function(value, property){
console.log(property + ':' + value);
if (value !== ''){
geojson = $filter('filter')(geojson, property, value);
}
});
function removeMarkers(data, map){
map.removeLayer(markerLayer);
markerLayer.ProcessView();
}
//map.removeLayer(markerLayer);
// Remove all the markers
$scope.geojson.data = geojson;
//renderMarkers(data.features, $scope.map);
} else {
$scope.geojson.data = $scope.data;
}
}, true);
}
]);
I think that the object that have to be modified in order to add popups is the actual prunecluster were you are registering the markers.
var markerLayer = new PruneClusterForLeaflet();
markerLayer.PrepareLeafletMarker = function (marker, data) {
if (marker.getPopup()) {
marker.setPopupContent(data.company);
} else {
marker.bindPopup(data.company);
}
};