i try to import GeoJson file to leaflet in angular's app 6.
With this solution my geojson is drawn in leafletmap but i have this error and i can't build my app. Someone know one solution ?
ERROR TS2345 Argument of type '{"type": string;"features":({"type":
string; "geometry": { "type: string : "coordinates": num...' is not
assignable parameter of type GeoJsonObject
Model.ts
export const Pdl = [ 'my geojson' ];
https://raw.githubusercontent.com/alanent/france-geojson/master/regions/pays-de-la-loire/departements-pays-de-la-loire.geojson
Component.ts
import { LeafletModule } from '#asymmetrik/ngx-leaflet';
import * as L from 'leaflet';
import {Pdl} from "../models/pdl.model";
#Component({
selector: 'app-geo',
templateUrl: './geo.component.html',
styleUrls: ['./geo.component.scss']
})
export class GeoComponent implements OnInit {
ngOnInit() {
var mapboxAccessToken = "...";
const myfrugalmap = L.map('frugalmap').setView([47.482019, -1], 7);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=' + mapboxAccessToken, {
id: 'mapbox.light',
attribution: 'SOS'
}).addTo(myfrugalmap);
L.geoJSON(Pdl).addTo(myfrugalmap);
}
}
Maybe, i can hide the error ? what is the way ?
You need to do it the 'angular way' since you are using ngx-leaflet
Importing a json via import is a nightmare in my personal opinion so what I would do would be to fetch it using a get request when the map is loaded and then get a reference to the map object and add the geojson.
template:
import { HttpClient } from '#angular/common/http';
import * as L from 'leaflet';
..
map: L.Map;
json;
options = {
layers: [
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18, attribution: '...'
})
],
zoom: 7,
center: L.latLng(47.482019, -1)
};
constructor(private http: HttpClient) { }
onMapReady(map: L.Map) {
this.http.get('assets/departements.json').subscribe((json: any) => {
console.log(json);
this.json = json;
L.geoJSON(this.json).addTo(map);
});
}
template
<div leaflet style="height: 800px"
[leafletOptions]="options"
(leafletMapReady)="onMapReady($event)">
</div>
Demo
Related
map.component.ts
export class MapComponent implements AfterViewInit {
private map;
private centroid: L.LatLngExpression = [49.2827, -123.1207];
private initMap(): void {
this.map = L.map('map', {
center: this.centroid,
zoom: 12
});
const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
minZoom: 3,
attribution: '© OpenStreetMap'
});
tiles.addTo(this.map);
}
constructor(private _dataService: DataService) {
}
ngAfterViewInit(): void {
this.initMap();
this._dataService.pigMessage$.subscribe(message => {
L.marker([49.2827, -123.1207]).addTo(this.map).bindPopup("test").openPopup();
console.log(message);
})
}
}
data.service.ts
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
import { pigReportType } from './pigReport';
#Injectable({
providedIn: 'root'
})
export class DataService {
private _pigMessageSource = new Subject<pigReportType>();
pigMessage$ = this._pigMessageSource.asObservable();
sendPigData(message: pigReportType){
this._pigMessageSource.next(message)
}
constructor() { }
}
When I click on the submit button, data service sends a new piece of information into the observable and redirects back to the map component. The problem is the marker is not adding inside of the subscribe function in map.component.ts.
Additionally, I have checked that the .subscribe function works because it prints the correct message. The markers just do not appear on the map.
I have tried looking through the html and seeing if there is a duplicated map that is covering the map with the marker but there isn't. Also I have tried to call initMap inside of the .subscribe function but it doesn't work.
I'm hoping if someone can point me in the right direction because I have searched everywhere on the web for a solution but can't find it.
I need to create a geo chart and I have installed the chartjs-chart-geo to implement a chart that simple show the total sales per state.
I got this error when trying to fetch the features from the topojson object and I got stack in here. Can any of you please help me ?
Error:
Property 'features' does not exist on type 'Feature<Point, GeoJsonProperties>'.
Here's my unfinish component code:
import { Component, OnInit } from '#angular/core';
import { DashboardService } from '../../../modules/dashboard.service';
import { Chart, registerables } from 'chart.js';
import { ChoroplethController, GeoFeature, ColorScale, ProjectionScale } from 'chartjs-chart-geo';
import * as ChartGeo from 'chartjs-chart-geo'
import ChartDataLabels from 'chartjs-plugin-datalabels';
// register controller in chart.js and ensure the defaults are set
Chart.register(ChoroplethController, GeoFeature, ColorScale, ProjectionScale);
Chart.register(...registerables);
Chart.register(ChartDataLabels);
// Get the topojson file examples
const url = 'https://unpkg.com/world-atlas#2.0.2/countries-50m.json';
// const url = 'https://unpkg.com/us-atlas/states-10m.json';
#Component({
selector: 'quotes-by-state',
templateUrl: './quotes-by-state.component.html',
styleUrls: ['./quotes-by-state.component.css']
})
export class QuotesByStateComponent implements OnInit {
chart:any;
constructor(private dashService: DashboardService) { }
ngOnInit(): void {
// Prepare chart
this.createChart();
}
// Create chart component
createChart() {
// fetch('https://unpkg.com/us-atlas/states-10m.json').then((r) => r.json()).then((us) => {
// const nation = ChartGeo.topojson.feature(us, us.objects.nation).features[0];
// const states = ChartGeo.topojson.feature(us, us.objects.states).features;
fetch(url).then((result) => result.json()).then((datapoint) => {
// Get ChartGeo features from json
const nation = ChartGeo.topojson.feature(datapoint, datapoint.objects.countries).features;
this.chart = new Chart('geo-chart', {
type: 'choropleth',
data: {
labels: ['a', 'b', 'c'],
datasets: [
{
label: 'Contries',
outline: nation,
data: null, //states.map(country => ({feature: country, value: Math.random() * 100})),
},
]
},
options: {
responsive: true,
}
});
});
}
}
I need to implement a button that download the polygon (or whatever shape) into a geojson file but the download isn't triggered. (trying with 2 different functions but both not working) i actually don't know if i got the functions wrong/missing something or the problem is in the .html
app.component.ts
import { Component } from '#angular/core';
import { latLng, tileLayer, DrawOptions, DrawEvents } from 'leaflet';
import { icon, marker, polyline, circle, rectangle, polygon} from 'leaflet';
import { LeafletModule } from '#asymmetrik/ngx-leaflet';
import { LeafletDrawModule } from '#asymmetrik/ngx-leaflet-draw';
import { DomSanitizer, SafeUrl, SafeResourceUrl } from '#angular/platform-browser';
import { FeatureGroup, featureGroup } from 'leaflet';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'tesiangularp';
options = {
layers: [
tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
})
],
zoom: 7,
center: latLng([ 41.471276, 12.907632 ])
};
layersControl = {
baseLayers: {
'Open Street Map': tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18, attribution: '...' }),
'Satellite View Map': tileLayer('http://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', {
maxZoom: 18, attribution: '...', subdomains:['mt0','mt1','mt2','mt3']})
},
overlays: {
}
}
drawnItems: FeatureGroup = featureGroup();
drawOptions = {
edit: {
featureGroup: this.drawnItems
},
Draw: {
}
};
public onDrawCreated(e: any) {
this.drawnItems.addLayer((e as DrawEvents.Created).layer);
}
geoExport =() => {
let nodata = '{"type":"FeatureCollection","features":[]}';
let jsonData = (JSON.stringify(this.drawnItems.toGeoJSON()));
let dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(jsonData);
let datenow = new Date();
let datenowstr = datenow.toLocaleDateString('en-GB');
let exportFileDefaultName = 'export_draw_'+ datenowstr + '.geojson';
let linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
if (jsonData == nodata) {
alert('No features are drawn');
} else {
linkElement.click();
}
}
exportJson() {
var data = this.drawnItems.toGeoJSON();
var convertedData = 'text/json;charset=UTF-8,' + encodeURIComponent(JSON.stringify(data));
document.getElementById('export')?.setAttribute('href', 'data:' + convertedData);
document.getElementById('export')?.setAttribute('download', 'data.json');
}
}
app.component.html
<div class="map"
leaflet
leafletDraw
[leafletOptions]="options"
[leafletLayersControl]="layersControl"
[leafletDrawOptions]="drawOptions"
(leafletDrawCreated)="onDrawCreated($event)">
<div [leafletLayer]="drawnItems"></div>
</div>
<a>
<div style="margin: 2px 0px 2px 0px;">
<button onclick= "exportJson()">Download</button>
</div>
</a>
There are differences in how browsers handle client side file creation. So, I would use a library called file-saver.
npm install file-saver
Then import it into the component
import { saveAs } from 'file-saver';
And save your geojson
const geojson = this.drawnItems.toGeoJSON();
const blob = new Blob([geojson], {type: 'application/json;charset=utf-8'});
saveAs(blob, 'drawnItems.geojson');
As per ngx-leaflet documentation link (leafletMapMoveEnd) and (leafletMapZoomEnd) are both exposed events.
I'm assuming that these events are exposed in the same DOM that the map is Initialized, and should be implemented like this:
<div
leaflet
[leafletOptions]="options"
(leafletMapReady)="onMapReady($event)"
(leafletMapMoveEnd)="onMapMove($event)"
(leafletMapZoomEnd)="onMapZoom($event)">
Is this the correct way to catch these events?
(leafletMapReady) seems to be working just fine.
However neither (leafletMapZoomEnd) or (leafletMapMoveEnd) seem to be triggering when I mess with the map it self.
I tried panning the map, as well as zooming in and out. Neither of those interactions cause the handleMapZoomEnd($event) handleMapMoveEnd($event) methods to be hit.
import { Component, Input, OnChanges, OnInit, Output, EventEmitter } from '#angular/core';
import * as fromLeafLet from 'leaflet';
import 'leaflet.markercluster';
#Component({
selector: 'map',
templateUrl: './map.component.html',
styleUrls: [
'./map.component.css',
'./extra-marker-icon.css'
]
})
export class MapComponent implements OnInit, OnChanges {
constructor(){}
onMapReady(map: fromLeafLet.Map): void {
this.map = map;
}
onMapZoom(event: any):void{
console.log('Zoom');
this.onMapDirty.emit();
}
onMapMove(event: any):void{
console.log('Move');
this.onMapDirty.emit();
}
}
In the Github repo for the #asymmetrik/ngx-leaflet ngcli tutorial, I added a branch demo/events that shows a really simple example of using the zoom/move events.
git clone git#github.com:Asymmetrik/ngx-leaflet-tutorial-ngcli.git
git checkout demo/events
The files of interest are:
./src/app/app.component.html
./src/app/app.component.ts
The template (below) contains two handlers, one for each of the leafletMapMoveEnd and leafletMapZoomEnd outputs:
<div class="map"
leaflet
[leafletOptions]="options"
(leafletMapReady)="onMapReady($event)"
(leafletMapMoveEnd)="handleMapMoveEnd($event)"
(leafletMapZoomEnd)="handleMapZoomEnd($event)"
></div>
The component (below) just prints to the console on these events. I removed mostly everything that's not necessary to demo the events working.
import { Component } from '#angular/core';
import * as L from 'leaflet';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: [
'./app.component.css'
]
})
export class AppComponent {
streetMaps = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
detectRetina: true,
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
});
map: L.Map;
options = {
layers: [ this.streetMaps ],
zoom: 7,
center: L.latLng([ 46.879966, -121.726909 ])
};
onMapReady(map: L.Map): void {
this.map = map;
}
handleMapZoomEnd(map: L.Map):void{
console.log('onMapZoomEnd');
}
handleMapMoveEnd(map: L.Map):void{
console.log('onMapMoveEnd');
}
}
I'm using 2GIS maps API based on Leaflet library. There are few functions in MapService (such as addMarkerToList()) which need to be called while clicking on map.
But there is a problem: using this.mapService.addMarkerToList will caught an error:
ERROR TypeError: Cannot read property 'addMarkerToList' of undefined
So I use const self = this construction to avoid this problem.
Can it be solved by another way?
map.component.ts
import {Component, OnInit} from '#angular/core';
import * as DG from '2gis-maps';
import {MapService} from '../../services/map/map.service';
let currentUserPos;
#Component({
selector: 'app-map',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements OnInit {
constructor(private mapService: MapService) {}
ngOnInit() {
const self = this;
const map = DG.map('map', {
'center': [54.98, 82.89],
'zoom': 13,
'fullscreenControl': false
});
if (map) {
console.log('Map added');
}
map.locate()
.on('locationfound', function (e) {
currentUserPos = DG.marker([e.latitude, e.longitude]).addTo(map).bindLabel('Your location', {
static: true
});
const currentCenter = new DG.latLng(e.latitude, e.longitude);
console.log('Current position founded: ' + 'lat = ' + e.latitude + '; lng = ' + e.longitude);
map.setView(currentCenter, 16);
});
map.on('click', function (e) {
const marker = DG.marker([e.latlng.lat, e.latlng.lng]);
marker.addTo(map);
marker.on('click', function() {
marker.remove(map);
self.mapService.deleteMarkerFromList(e.latlng.lat, e.latlng.lng);
});
console.log('Marker after adding:' + marker.getLatLng());
self.mapService.addMarkerToList(e.latlng.lat, e.latlng.lng);
});
}
}