I am still new to Leaflet and have been teaching myself. I have made a map using the Leaflet Draw libraries which works well, and I would like to add a function that allows the user to save the drawn features to a GeoJSON file. I have tried using this guide but I am still not having any luck.
I am using a button for the export function:
<input type='button' id='export' value='Export Feature (.kml)' class='btn' />
With the following body:
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '© OpenStreetMap ',
osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib }),
map = new L.Map('map', { center: new L.LatLng(-29.5, 24.85), zoom: 6 }),
drawnItems = L.featureGroup().addTo(map);
L.control.layers({
'Street': osm.addTo(map),
"Satellite": L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri</a>'
})
}, {'Survey Area': drawnItems }, { position: 'topleft', collapsed: false }).addTo(map);
map.addControl(new L.Control.Draw({
edit: {
featureGroup: drawnItems,
poly: {
allowIntersection: false
}
},
draw: {
polygon: {
allowIntersection: false,
showArea: true
}
}
}));
map.on(L.Draw.Event.CREATED, function (event) {
var layer = event.layer;
drawnItems.addLayer(layer);
});
document.getElementById('export').onclick = function(e) {
// Extract GeoJson from featureGroup
var data = featureGroup.toGeoJSON();
// Stringify the GeoJson
var convertedData = 'text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(data));
// Create export
document.getElementById('export').setAttribute('href', 'data:' + convertedData);
document.getElementById('export').setAttribute('download','data.geojson');
}
I think the issue is with featureGroup or drawnItems but I just can't figure it out.
Related
I have a map with a number of pins on it, the pins are generated from an endpoint api (json). I want to filter the pins via an input that has a v-modal - the search criteria is already set up and is pulled from the same api.
Even if someone can give some tips as to where in the vue lifecycle the filter should happen, i.e mounted, updated computed ect
Originally I used this article as a reference
https://medium.com/#limichelle21/integrating-google-maps-api-for-multiple-locations-a4329517977a
created() {
axios
.get(
`https://cors-anywhere.herokuapp.com/myEndpoint`
)
.then(response => {
// JSON responses are automatically parsed.
this.allProperties = response.data;
this.markers = this.allProperties.map(function (x) {
return {
lat: parseFloat(x.lat).toFixed(3),
lng: parseFloat(x.lng).toFixed(3),
add: x.dispAddress,
propId: x.property_id,
propPrice: x.outputAskingPrice,
propImg: x.imagePath
};
});
this.allProperties = response.data.map(x => {
x.searchIndex = `${x.sellingStatus} ${x.priceType} ${x.typeNames[0]} ${x.typeNames[1]} ${x.dispAddress}`.toLowerCase();
return x;
});
});
},
mounted: function () {
var _this = this;
function initMap() {
var center = {
lat: 53,
lng: -3
};
var map = new google.maps.Map(document.getElementById("map-canvas"), {
zoom: 10,
center: center
});
var newPin = new google.maps.Marker({
position: center,
map: map
});
}
},
updated() {
var _this = this;
var map = new google.maps.Map(document.getElementById("map-canvas"), {
zoom: 9,
center: new window.google.maps.LatLng(55.961, -3)
});
var infowindow = new google.maps.InfoWindow({});
var newPin;
var count;
for (count = 0; count < _this.markers.length; count++) {
newPin = new google.maps.Marker({
position: new google.maps.LatLng(
_this.markers[count].lat,
_this.markers[count].lng
),
map: map,
icon: "../assets/img/map-pin.png"
});
google.maps.event.addListener(
newPin,
"click",
(function (newPin, count) {
return function () {
infowindow.setContent(` ${_this.markers[count].add} <p> ${_this.markers[count].propPrice}</p><img src="${_this.markers[count].propImg}"><p>`);
infowindow.open(map, newPin);
};
})(newPin, count)
);
}
If you have v-model on an <input> field like mentioned in your question, you are binding the value of this <input> field to a variable probably defined in the data part of your Vue component. The value is always up to date in the model (reactive binding). You can watch this value and then trigger a function which updates Google Maps. Here is an example:
Vue.component('demo', {
data () {
return {
inputField: ''
};
},
created () {
console.log('Component script loaded, HTML not yet ready, load the data from your backend. Use a flag like isLoading or similar to indicate when the data is ready to enable input.');
},
mounted () {
console.log('Component mounted, HTML rendered, load Google Maps');
},
watch: {
inputField (newValue) {
console.log(`inputField changed to ${newValue}. Trigger here a method which update Google Maps. Make sure to debounce the input here, so that it does not trigger a Google Maps update too often.`);
}
},
template: `
<div>
<input type="text" v-model="inputField" placeholder="Lookup place">
</div>`
});
new Vue({ el: '#vue-demo-container' });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vue-demo-container">
<demo></demo>
</div>
Please i want to display a layer from GeoJSON file with openlayers 4.3.1 but the result (the vectorLayer variable) show a blank page. What am i missing?
The GeoJSON file is here https://gist.githubusercontent.com/abdounasser202/5d830738ad29e6395743530545bd322b/raw/0c34d9a1ced1879432318b6860c1c21e8e88ef04/quartiers_yaounde.geojson
Here is my JS code
var styles = {
'Polygon': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'grey',
width: 1
}),
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
})
}),
};
var styleFunction = function(feature) {
return styles[feature.getGeometry().getType()];
};
var geojsonObject = 'https://gist.githubusercontent.com/abdounasser202/5d830738ad29e6395743530545bd322b/raw/0c34d9a1ced1879432318b6860c1c21e8e88ef04/quartiers_yaounde.geojson';
console.log("geodata >>> ", geojsonObject);
var vectorSource = new ol.source.Vector({
format: new ol.format.GeoJSON(),
url: geojsonObject
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: styleFunction
});
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
// var extent_degree = [11.4095, 3.71349, 11.5747, 3.9692];
var map = new ol.Map({
layers: [vectorLayer],
target: 'map',
view: new ol.View({
// projection: 'EPSG:4326',
center: ol.proj.fromLonLat([11.5021, 3.8480]),
zoom: 6
})
});
// ol.proj.get('EPSG:4326').setExtent(extent_degree)
var units = map.getView().getProjection().getUnits();
console.log("units >>> ", units);
var projections = map.getView().getProjection();
console.log("projections >>> ", projections);
According your code, you are styling Polygons, but no other geometry types. So you will see only Polygons on your map. Other geometry types will be invisible since they are not styled. The GeoJSON file does not contain Polygons, so indeed you won't see anything.
As far as I can see, the GeoJSON file contains only MultiPolygons. To make them visible, add a style for 'MultiPolygon' in the variable styles, or just change the string 'Polygon' in your code to 'MultiPolygon'.
I have a problem with angular 2 and the API v3 of google maps, the problem is that when I load the page through the router outlet, the map is a grey box, but when I refresh the page the map load correctly, I'm using the js library import on the index page, and the map is loaded in a component.
this is the view of the map
I tried with trigger rezise but don't work. I think that is a problem with the library load or similar.
this is the code:
import {Component, OnInit} from "angular2/core";
import {MapsService} from "../maps/maps.service";
import {mapsIconService} from "./maps.icon.service";
import {BrowserDomAdapter} from 'angular2/platform/browser';
#Component({
selector: "div-map",
template: `
<div id="map" style="height: 500px" class="maps"> </div> `,
providers: [MapsService, mapsIconService, BrowserDomAdapter],
style: `
.maps{
overflow:visible;
}
`
})
export class Maps {
map;
snappedCoordinates = [];
points:string = "";
map:Map;
drawingManager;
placeIdArray = [];
polylines = [];
markers = [];
placeIds = [];
infoWindows = [];
snappedPolyline;
showMap = false;
lineSymbol = {
path: google.maps.SymbolPath.CIRCLE,
scale: 8,
strokeColor: '#005db5',
strokeWidth: '#005db5'
};
mapOptions = {
zoom: 15,
center: {lat: 37.38658313258027, lng: -122.05207727132837},
};
constructor(private _dom:BrowserDomAdapter, private _mapService:MapsService, private _mapIconService:mapsIconService) {
// google.maps.visualRefresh = true;
}
drawSnappedPolyline(snappedCoordinates) {
this.snappedPolyline = new google.maps.Polyline({
path: snappedCoordinates,
strokeColor: 'black',
strokeWeight: 3
});
this.polylines.push(this.snappedPolyline);
}
animateCircle(polyline) {
var count = 0;
// fallback icon if the poly has no icon to animate
var defaultIcon = [
{
icon: this.lineSymbol,
offset: '100%'
}
];
window.setInterval(function () {
count = (count + 1) % 300;
var icons = defaultIcon;
icons[0].offset = (count / 3) + '%';
polyline.set('icons', icons);
}, 300);
}
setMarker(lat, lng, id) {
var marker = new google.maps.Marker({
position: this.map.getCenter()/*new google.maps.LatLng(lat, lng)*/,
icon: {url: 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(this._mapIconService.getIcon(id))},
draggable: false,
map: this.map
});
}
SetAllMarkers(points) {
for (var i in points) {
this.setMarker(points[i].lat, points[i].lng, points[i].id);
}
}
ngOnInit() {
this.map = new google.maps.Map(this._dom.query("#map"), this.mapOptions);
google.maps.event.trigger(this.map, 'resize');
this._mapService.goData().subscribe(data=> {
this.drawSnappedPolyline(this._mapService.processSnapToRoadResponse(data));
this.setMarker(37.38658313258027, -122.05207727132837, 0);
this.snappedPolyline.setMap(this.map);
this.animateCircle(this.snappedPolyline);
});
}
}
any one have a idea about this? thank in advance
the problem was semantic UI, because he insert the code into a "pusher" and this cause a problem with the all elements
I have a website built from Google Maps and using MarkerClusterer. This site works fine most of the time, but there will be times when multiple users will be online and a filtered marker sets request will be returned incorrectly.
Example: I filter to a set of markers based on a set of filter parameters. As this is processing someone else also filters to a different set of markers and those markers are returned to me instead of my request.
I can also see this issue occur if I sit on the page and just hit refresh. I have tested in our test environment with 2 users. By having one user filter to a set of markers and when the second user refreshes their browser window they see the same set of filtered markers. The markers of the first user.
I have seen example fixes were it is discussed to use a CacheBuster by adding the Date to the URL, but in using the MarkerCluserer code I do not see where to add that as an option. It has also been asked not to do this.
Data is coming from the server in an array object. markerclusterer.js is located locally in the Visual Studio Project. Here is the Script on the page that generates the map:
<script type="text/javascript" src="http://maps.google.com/maps/api/js?v=3.11&sensor=false"></script>
<script type="text/javascript" src="/Scripts/markerclusterer.js"></script>
<script type="text/javascript">
var infowindow;
var map;
var markerdataLength; //en - 5-8-2013
var prev;
$(document).ready(function () {
setTimeout("gmarkers()", 500);
});
function gmarkers() {
markerdataLength = 0;
$("#map_canvas").fadeTo(500, 0.3);
$("#pwait").show();
$.ajax({
type: "POST",
url: "MapSearch.aspx/GetMarkers",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
$("#map_canvas").fadeTo(0, 0.3);
$("#pwait").show();
var markerdata;
markerdata = msg.d;
msg = null;
var latlng = new google.maps.LatLng(52.50, -98.35);
var myOptions = {
zoom: 3,
center: latlng,
//mapTypeId: google.maps.MapTypeId.HYBRID,
mapTypeId: google.maps.MapTypeId.TERRAIN,
mapTypeControl: true,
mapTypeControlOptions: {
position: google.maps.ControlPosition.TOP_RIGHT
},
panControl: true,
panControlOptions: {
position: google.maps.ControlPosition.TOP_LEFT
},
zoomControl: true,
zoomControlOptions: {
position: google.maps.ControlPosition.TOP_LEFT
},
scaleControl: true,
scaleControlOptions: {
position: google.maps.ControlPosition.BOTTOM_CENTER
},
streetViewControl: true,
streetViewControlOptions: {
position: google.maps.ControlPosition.TOP_LEFT
}
};
//initialize the info window (balloon popup)
infowindow = new google.maps.InfoWindow();
//draw the map with the above settings
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
/***************************MARKER CLUSTERER **********************************************/
var imageUrl = 'http://chart.apis.google.com/chart?cht=mm&chs=24x24&chco=' +
'FFFFFF,008CFF,000000&ext=.png'; //Blue
var markers = [];
if (markerdata != "undefined" & markerdata.length > 0) { //run this code if the data is not null. data is the markers in json format
markerdataLength = markerdata.length; //en - 5-8-2013
var i = 0;
var ro = new RepeatingOperation(function () {
var dataWell = markerdata[i];
var mlatLng = new google.maps.LatLng(dataWell.la, dataWell.lo);
var marker = new google.maps.Marker({
id: dataWell.id,
position: mlatLng,
title: dataWell.t,
icon: imageUrl //iconstring
});
//set the marker click code to show the info window
var fn = markerClick(dataWell.id);
google.maps.event.addListener(marker, 'click', fn);
google.maps.event.addListener(marker, 'clusteringend', clusteringDone()); //en - 5-8-2013
markers.push(marker);
if (++i < markerdata.length) { ro.step(); }
else {
var mcOptions = {
gridSize: 90, //default grid size is 60 was using 35. higher #'s draw faster.
maxZoom: 20, //defines how close u are before no more clusters are drawn
minimumClusterSize: 15
};
var mc = new MarkerClusterer(map, markers, mcOptions);
}
}, 100);
ro.step();
}
else {
alert("No wells were found.");
$("#map_canvas").fadeTo(300, 1);
$("#pwait").hide();
}
}
});
}
I'm trying to display an embeded Google Map based on the location of an Event object.
This is the basic app:
App = Em.Application.create();
App.Event = Em.Object.extend({
lat: -33.8665433,
lng: 151.1956316
});
App.EventController = Ember.ObjectController.extend();
App.ApplicationController = Ember.ObjectController.extend();
App.EventView = Ember.View.extend({
templateName: 'event'
});
App.ApplicationView = Ember.View.extend({
templateName: 'application'
});
App.Router = Ember.Router.extend({
enableLogging: true,
root: Ember.Route.extend({
event: Ember.Route.extend({
route: '/',
connectOutlets: function (router) {
router.get('eventController').set('content', App.Event.create());
router.get('applicationController').connectOutlet('event');
}
})
})
});
App.initialize();
With the following templates:
<script type="text/x-handlebars" data-template-name="application">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="event">
{{lat}} {{lng}}
// Embeded Google Map
</script>
Where would I initialize the map? Additionally, if lat/lang change, how would I catch it and redraw the map?
Working View Code (Modified from sabithpocker's answer)
App.EventView = Ember.View.extend({
templateName: 'event',
map: null,
latitudeBinding: 'controller.content.lat',
longitudeBinding: 'controller.content.lng',
didInsertElement: function () {
var mapOptions = {
center: new google.maps.LatLng(this.get('latitude'), this.get('longitude')),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(this.$().get(0), mapOptions);
this.set('map', map); //save for future updations
this.$().css({ width: "400px", height: "400px" });
},
reRenderMap: function () {
if (this.get('map')) {
var newLoc = new google.maps.LatLng(this.get('latitude'), this.get('longitude'));
this.get('map').setCenter(newLoc);
}
}.observes('latitude', 'longitude') //these are bound to lat/lng of Event
});
Here is a quick idea, not following ember app structure, just an idea.
App.EventView = Ember.View.extend({
templateName: 'event',
map : null,
latitudeBinding : 'App.Event.lat',
longitudeBinding : 'App.Evet.lng',
didInsertElement : function(){
var mapOptions = {
center: new google.maps.LatLng(this.get('latitude'), this.get('longitude')),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(this.$().get(0),mapOptions);
this.set('map',map); //save for future updations
},
reRenderMap : function(){
var newLoc = new google.maps.LatLng(this.get('latitude'), this.get('longitude'));
this.get('map').setCenter(newLoc)
}.observes('latitude','longitude') //these are bound to lat/lng of Event
});
Also I think App.Event should be:
App.Event = Em.Object.extend({
lat: null,
lng: null,
init : function(){
this.set('lat', -33.8665433);
this.set('lng', 151.1956316);
}
});
to avoid the so-called chromosomal mutation in Ember