wykks / ngx-mapbox-gl Goto Github PK
View Code? Open in Web Editor NEWAngular binding of mapbox-gl-js
Home Page: https://wykks.github.io/ngx-mapbox-gl
License: MIT License
Angular binding of mapbox-gl-js
Home Page: https://wykks.github.io/ngx-mapbox-gl
License: MIT License
When changing the zoom level in the browser, the canvas height / width no longer accurately describes the accurate viewport box of what objects should be visible.
See: https://github.com/stepankuzmin/mapbox-gl-js/blob/master/src/ui/map.js#L1523
The result of this is that when markers or clusters are added to the maps, if the user changes the zoom level of the browser, then markers disappear from the map when they should still be visible.
For example, go to the demo here:
https://wykks.github.io/ngx-mapbox-gl/demo/ngx-marker-cluster
and set the zoom on the browser to 50%, you will see half the of the clusters completely disappear.
In order to get an accurate representation of the viewport box, the style property should be used to get the viewport box.
First of all, thanks for the library, it made my day!
I need to update a geojson layer and fit it's bounds, when new data arrived.
Do you think there is a simple way to achieve this?
What do you think of replacing yarn with npm.
npm is the standard node package manage and imho any library should use it by default :)
When using localhost as the hostname, and navigating to another route, everything works fine and the map destroys itself.
When using the hostname though, when navigating to a new route, this error is thrown:
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'remove' of undefined TypeError: Cannot read property 'remove' of undefined at i.onRemove (mapbox-gl.js:32) at n.removeControl (mapbox-gl.js:32) at ngx-mapbox-gl.js:255 at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:388) at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:138) at NgZone.push../node_modules/@angular/core/fesm5/core.js.NgZone.runOutsideAngular (core.js:4021) at MapService.push../node_modules/ngx-mapbox-gl/esm5/ngx-mapbox-gl.js.MapService.removeControl (ngx-mapbox-gl.js:254) at ControlComponent.push../node_modules/ngx-mapbox-gl/esm5/ngx-mapbox-gl.js.ControlComponent.ngOnDestroy (ngx-mapbox-gl.js:667) at callProviderLifecycles (core.js:10420) at callElementProvidersLifecycles (core.js:10388) at i.onRemove (mapbox-gl.js:32) at n.removeControl (mapbox-gl.js:32) at ngx-mapbox-gl.js:255 at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:388) at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:138) at NgZone.push../node_modules/@angular/core/fesm5/core.js.NgZone.runOutsideAngular (core.js:4021) at MapService.push../node_modules/ngx-mapbox-gl/esm5/ngx-mapbox-gl.js.MapService.removeControl (ngx-mapbox-gl.js:254) at ControlComponent.push../node_modules/ngx-mapbox-gl/esm5/ngx-mapbox-gl.js.ControlComponent.ngOnDestroy (ngx-mapbox-gl.js:667) at callProviderLifecycles (core.js:10420) at callElementProvidersLifecycles (core.js:10388) at resolvePromise (zone.js:814) at resolvePromise (zone.js:771) at zone.js:873 at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421) at Object.onInvokeTask (core.js:4053) at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:420) at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:188) at drainMicroTaskQueue (zone.js:595) at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (zone.js:500) at invokeTask (zone.js:1540)
I've tested with both IIS and http-server with the same results. Any idea how to get this to work?
as said, I'm trying to pass a nested Object
myObj = {id: 123, data: {name: 'foo', surname: 'bar'}}
to the mgl-feature [properties] input to be later able to populate a popup with that data. But unfortunately the object from second level on (myObj.data) gets stringified. Any ideas (or suggestions for different ways very welcome.
I trying to make a canvas source work with a layer but it seem error always occur when I try to add the layer.
<canvas class="mycanvas" id="mycanvas" #mycanvas></canvas>
<mgl-map (load)="mapLoaded($event)" [style]="'mapbox://styles/mapbox/streets-v9'" [zoom]="[5]" [center]="[-75.789, 41.874]">
<mgl-canvas-source id='canvasSource' #canvasSource [canvas]='mycanvas' [coordinates]="[
[-81.490, 46.437],
[-72.582, 46.437],
[-72.582, 38.907],
[-81.490, 38.907]
]"></mgl-canvas-source>
<!-- add this line will occur error -->
<mgl-layer id="canvasLayer" type="raster" [source]="canvasSource" [paint]="{'raster-opacity':0.85}"></mgl-layer>
</mgl-map>
Hey,
when you change the mgl-map style and you have a layer with markers p.e. the markers get lost. I could bet that this is a z-index issue. I will try to find a bugfix.
You can reproduce the issue be adapting the "change a maps style" demo according to this:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'mgl-demo',
template: `
<mgl-map
[style]="style"
[zoom]="[13]"
[center]="[-71.976080,-13.521338]"
>
<mgl-vector-source
id="museums"
url="mapbox://mapbox.2opop9hr"
>
</mgl-vector-source>
<mgl-layer
id="museums"
type="circle"
source="museums"
[paint]="{
'circle-radius': 8,
'circle-color': 'rgba(55,148,179,1)'
}"
sourceLayer="museum-cusco"
>
</mgl-layer>
</mgl-map>
<mat-radio-group [ngModel]="layerId" (ngModelChange)="changeStyle($event)">
<mat-radio-button value="basic">basic</mat-radio-button>
<mat-radio-button value="streets">streets</mat-radio-button>
<mat-radio-button value="bright">bright</mat-radio-button>
<mat-radio-button value="light">light</mat-radio-button>
<mat-radio-button value="dark">dark</mat-radio-button>
<mat-radio-button value="satellite">satellite</mat-radio-button>
</mat-radio-group>
`,
styleUrls: ['./examples.css', './set-style.component.css']
})
export class SetStyleComponent implements OnInit {
layerId = 'basic';
style: string;
ngOnInit() {
this.changeStyle(this.layerId);
}
changeStyle(layerId: string) {
this.style = `mapbox://styles/mapbox/${layerId}-v9`;
}
}
cli v1.7.2
"mapbox-gl": "^0.44.2",
"mapbox-gl-geocoder": "^2.0.1",
"ngrx-store-freeze": "^0.2.1",
"ngx-mapbox-gl": "^1.0.0-rc.2",
I am using ionic 3. I have installed module as mentioned in readme file.
I am getting error:
Uncaught Error: Cannot find module "mapbox-gl"
at Object.defineProperty.value (vendor.js:125364)
at webpack_require (bootstrap c6e261b75fefa3a6dcec:54)
at Object.241 (main.ts:5)
at webpack_require (bootstrap c6e261b75fefa3a6dcec:54)
at Object.227 (main.js:80)
at webpack_require (bootstrap c6e261b75fefa3a6dcec:54)
at webpackJsonpCallback (bootstrap c6e261b75fefa3a6dcec:25)
at main.js:1
Hey, thanks for your great work!
Just tried and f.e. trackResize
seems to do nothing and I couldn't get mgl-geojson-source
to work either :(
A note in the wiki which features are done would be awesome!
Hi,
How to add global css in ionic 3 application. Because in ionic 3 angular-cli.json in not available. Can you please help.
Regards and thanks
Khurshid Ansari
I was having an issue where the css was giving a 404, I was importing it as per the instructions:
@import "~mapbox-gl/dist/mapbox-gl.css";
The fix was to remove the .css
from the end of the import statement.
@import "~mapbox-gl/dist/mapbox-gl";
I'm not sure if this is implementation specific (I am using Angular full-stack generator ), which uses SCSS/webpack/angular5. Or if this is simply an error in your documentation.
I am using ngx-mapbox-gl:1.0.0-beta.5 and I can't import MapService to my component. If i write like this
import {MapService} from 'ngx-mapbox-gl/src/app/lib/map/map.service';
i have an error while compiling
Module not found: Error: Can't resolve 'ngx-mapbox-gl/src/app/lib/map/map.service'
if i change import to
import {MapService} from 'ngx-mapbox-gl/bundles/ngx-mapbox-gl.umd.js';
Compiled successfully. But i have errors in browser console like
core.js:1440 ERROR Error: Uncaught (in promise): Error: Can't resolve all parameters for MyComponent:
I think that it is an error in ng-packagr or something with it's config. Because if i use your source files in my app everything is fine and work as expected.
I've taken a stab at this a couple of times and at the moment it appears to be mapbox dependent. The best way I found was to not use the typescript helpers for mapbox code and instead just require it after a isPlatformBrowser test but this is contrived.
I'm not expecting this to be an easy fix but a placeholder here for discussion seems appropriate.
I have an app that I am doing server side rendering using this library but I have to instantiate a jsdom instance and put it in place for global.window and global.Blob which is squirrely as a number of library check that window or window.document are non-existent to enable their server side code paths.
Hi,
I am developing hybrid app using ionic 3 and angular 4. Which one should i used for ionic application ngx-mapbox-gl vs telerik plugin?
I am new here.
Can you help how to basic methods and properties of mapbox object like
map.flyTo({
center:[54,34],
zoom:17
});
I've tried to add ngx-mapbox-gl to a couple of projects of mine but I'm stuck on Change Detection Issues. It's been very difficult to debug cause I'm not getting any error messages in the console.
I've created a Stackblitz to demonstrate some areas of confusion. Basically, this app just toggle's an Angular Material Side-navigation component in and out on click. Different click events for the boilerplate on top and the mgl-map on the bottom.
My first question relates to line: 58 in app.component.ts
Why do I need to detect for changes using Angular's ChangeDetectionRef in order for the sidenav to open and close?
I attempting to load 3D buildings using the source provide at mapbox 3D buildings . However, I don't see api documentation on how to pass an event to the map component via ngx-mapbox-gl. Is this possible? Looking over a few examples, I notice that load is being used, but it isn't explicitly clear, to me, how it functions.
Taken from language switch component
<mgl-map
style="mapbox://styles/mapbox/light-v9"
[zoom]="[2.9]"
[center]="[16.05, 48]"
(load)="map = $event"
>
Any input would be appreciated.
I just tried this several ways, precisely as described and with some tweaks, and the map simply never shows up. I get no console errors unless I try to put the map in the component and listen for onLoad in which case map is undefined and if I try to initialize it (map: Map = new Map()) then I get other errors.
app.module.ts:
import { NgxMapboxGLModule } from 'ngx-mapbox-gl';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
NgxMapboxGLModule.forRoot({
accessToken: 'MY_TOKEN'
})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.component.html (also tried without the load event):
<mgl-map [style]="'mapbox://styles/mapbox/streets-v9'"
[zoom]="9"
[center]="[-74.50, 40]"
(load)="map = $event"></mgl-map>
app.component.css (also tried with 100%):
mgl-map {
height: 100vh;
width: 100vw;
}
app.component.ts (also tried with no map code):
map: Map;
ngOnInit() {
this.map.on('load', e => {
console.log(e);
});
}
I was really hoping to use this in a big project. Seems like it would be super-useful. I'm hoping I am just missing something or some recent release just broke it temporarily?
the example in the docs is not descriptive on what prameters should i send in order to use the map or the reduce function
The result is this error:
ERROR in node_modules/ngx-mapbox-gl/lib/source/geojson/geojson-source.component.d.ts(2,10): error TS2305: Module '"node_modules/@types/mapbox-gl/index"' has no exported member 'GeoJSONGeometry'.
Current bundle:
"dependencies": {
"@agm/core": "^1.0.0-beta.2",
"@agm/snazzy-info-window": "^1.0.0-beta.2",
"@angular/animations": "^5.0.0",
"@angular/cdk": "^5.0.0-rc0",
"@angular/common": "^5.0.0",
"@angular/compiler": "^5.0.0",
"@angular/core": "^5.0.0",
"@angular/forms": "^5.0.0",
"@angular/http": "^5.0.0",
"@angular/material": "^ 5.0.0-rc0",
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/router": "^5.0.0",
"@ngrx/effects": "^4.1.1",
"@ngrx/router-store": "^4.1.1",
"@ngrx/store": "^4.1.1",
"@ngrx/store-devtools": "^4.1.1",
"@ngx-translate/core": "^8.0.0",
"@ngx-translate/http-loader": "^2.0.0",
"@swimlane/ngx-charts": "^6.1.0",
"angular2-token": "^0.2.0-beta.12",
"bootstrap": "^4.0.0-beta",
"core-js": "^2.4.1",
"d3": "^4.11.0",
"express": "^4.15.3",
"font-awesome": "^4.7.0",
"hammerjs": "^2.0.8",
"mapbox-gl": "^0.42.0",
"ngrx-store-localstorage": "^0.2.1",
"ngx-mapbox-gl": "^1.0.0-beta.1",
"ngx-toastr": "^6.5.0",
"primeng": "^5.0.0-rc.0",
"rxjs": "^5.5.2",
"snazzy-info-window": "^1.1.0",
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular/cli": "1.5.0",
"@angular/compiler": "^5.0.0",
"@angular/compiler-cli": "^5.0.0",
"@angular/language-service": "^5.0.0",
"@types/jasmine": "~2.5.53",
"@types/jasminewd2": "~2.0.2",
"@types/node": "~6.0.60",
"codelyzer": "~3.2.0",
"jasmine-core": "~2.6.2",
"jasmine-spec-reporter": "~4.1.0",
"karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1",
"karma-cli": "~1.0.1",
"karma-coverage-istanbul-reporter": "^1.2.1",
"karma-jasmine": "~1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.1.2",
"ts-node": "~3.2.0",
"tslint": "~5.7.0",
"typescript": "~2.4.2"
}
}
I get this when trying to use the module:
node_modules/ngx-mapbox-gl/src/app/lib/map/map.service.d.ts(49,25): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/marker/marker.component.d.ts(7,15): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/marker/marker.component.d.ts(7,31): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/feature.component.d.ts(3,69): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/feature.component.d.ts(3,85): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/feature.component.d.ts(6,15): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/geojson-source.component.d.ts(10,12): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/geojson-source.component.d.ts(10,28): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/geojson-source.component.d.ts(10,54): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/geojson-source.component.d.ts(10,80): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/geojson-source.component.d.ts(25,25): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/geojson-source.component.d.ts(25,41): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/geojson-source.component.d.ts(26,28): error TS2503: Cannot find namespace 'GeoJSON'.
node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/geojson-source.component.d.ts(26,44): error TS2503: Cannot find namespace 'GeoJSON'.```
How can I get access to mapboxgl using ngx-mapbox-gl?
i have been working with a project involving loading a dashboard lazy loaded module which contains a map component.
expected behaviour is that once navigate to another modules or children components, the map component should gets destroyed, leaving nothing behind in memory.
what happens is that when you navigate back to the dashboard module, a new instance of the map component along with the map service get initiated causing a rise in memory usage overtime when you navigate through the app different pages, because the map component doesn't get destroyed or garbage collected, reducing performance significantly over time.
i experienced this in Angular 5.2.2 using just mapbox-gl 0.46.0, and now when i tried using ngx-mapbox-gl i found the same behaviour.
a reproducing repo
When you transition to a new route with 1.0.0.beta.3 and you have a cluster on a geoJSON source the current behavior is to remove the source with results in
mapbox-gl.js:546 Error: Source "properties" cannot be removed while layer "clusters" is using it.
at t.removeSource (mapbox-gl.js:406)
at e.removeSource (mapbox-gl.js:520)
at ngx-mapbox-gl.js:470
If feels like you have fixed this with the new cluster mechanism for beta.4 + but I haven't been able to test it yet.
The title describes fairly accurately what I'm experiencing. I would like to be able to click on a map and have a map marker move to that location.
Here's my template:
<mgl-map class=""
[style]="'mapbox://styles/mapbox/streets-v9'"
[zoom]="[9]"
[center]="[153.021072, -27.470125]"
(click)="mapClick($event)"
>
<mgl-marker
[feature]="pickerLocation"
>
<div
(click)="alert('Foo')"
class="marker"
>
<i class="fa fa-map-marker"></i>
{{pickerLocation.geometry.coordinates}}
</div>
</mgl-marker>
</mgl-map>
Here's the important bits from my controller:
pickerLocation = {
type: "Feature",
geometry: {
type: "Point",
coordinates: [153.021072, -27.470125]
}
};
mapClick(event) {
if(event.lngLat){
this.pickerLocation.geometry.coordinates = event.lngLat;
}
console.log(this.pickerLocation);
}
When I click on the map the object coordinates property is being updated, this can be confirmed by seeing the template binding {{pickerLocation.geometry.coordinates}}
updating, however the markers position isn't changing at all.
I've tried replacing the mgl-marker data source to a plain array or object, instead of a Feature. However this is having the same effect (the pin doesn't move on click).
On the API documentation for the mgl-marker
component you write:
Note: Only use this if you really need to use HTML/Angular component to render your symbol. These markers are slow compared to a Layer of symbol because they're not rendered using WebGL.
Could you provide an example on how to accomplish this and maybe how to attach a popup to a marker created like this?
Thanks a lot for this library!
The wiki doesn't explicit tells that it's required, but more so:
LayerComponent {
[...]
@Input() type?: 'symbol ....'
}
type
shouldn't be optional.Tested with demo/geojson-line : removing type: "line"
results in no line drawn.
@Wykks
I'm using this current WeatherImageryFeed. I'm curious about it's rendering performance. It's so slow. Is there a better way to retrieve it or could there be a bug in mapbox-gl?
Thanks
<mgl-raster-source
id="radar"
[tiles] = "['https://nowcoast.noaa.gov/arcgis/services/nowcoast/radar_meteo_imagery_nexrad_time/MapServer/WmsServer?bbox={bbox-epsg-3857}&service=WMS&request=GetMap&version=1.3.0&layers=1&styles=&format=image/png&transparent=true&height=256&width=256&crs=EPSG:3857']"
[tileSize] = "256">
</mgl-raster-source>
All I see is:
WARNING: sanitizing unsafe style value mapbox://styles/mapbox/dark-v9 (see http://g.co/ng/security#xss).
Dom in devtools shows nothing for mgl-map
After upgrade to angular 6:
ERROR in node_modules/ngx-mapbox-gl/app/lib/map/map.service.d.ts(5,10): error TS2305: Module '".... /node_modules/rxjs/Observable"' has no exported member 'Observable'
Dynamically changing this.center
when
<mgl-map
...
[center]="center"
[zoom[="zoom"
...
resets the zoom to inital value - same the other way round.
(can be seen in demo: examples/center-on-symbol.component)
Expected behaviour:
Changing center/zoom shouldn't affect the zoom/center
How would i hide the compass from the navigation control ?
I'm trying to use the <mgl-marker-cluster>
and set the markers in it with anchor and offset options. Is this posible?
Because marker added in this detectChange()
won't run inside NgZone, which is bad when using events in marker child for example
Hi there, me again...
Found another problem with the docs. I've never edited a github wiki before but it seems maybe I don't have privilege?
Anyway, your sample code here: https://github.com/Wykks/ngx-mapbox-gl/wiki/API-Documentation#example-1
source needs to be in square brackets (at least for me anyway) I was using a polygon.
...
<mgl-map
...
>
<mgl-layer
id="state-borders"
type="line"
[source]="states"
[paint]="{
'line-color': '#627BC1',
'line-width': 2
}"
></mgl-layer>
</mgl-map>
I have a Shared Module which imports NgxMapboxGLModule without calling forRoot.
The cli builds this package correctly with the --prod flag.
The app module which uses this shared module imports NgxMapboxGLModule.forRoot(). During production build using
ng build --prod
The following error is displayed:
ERROR in ./node_modules/@sharedmodule/shared/sharedmodule-shared.ngfactory.js Module not found: Error: Can't resolve './node_modules/ngx-mapbox-gl/ngx-mapbox-gl.ngfactory' in 'c:\TFS\DataLayers\UI\node_modules\@sharedmodule\shared'
Any idea what could be causing this, or is version 1.2.0 not ready for optimization with Cli version 6+?
Any idea if cli 6.1 beta would work?
after installing and add to app.module - multiple errors appear :'Cannot find namespace "GeoJSON"
Is there a way to specify the model for <mgl-control mglGeocoder>
?
Scenario:
dialogRef.afterClosed().subscribe(addr => {
this.geocoderInput = addr;
/* this.geocoder.query(addr); */
});
Template:
`<mgl-control mglGeocoder [(ngModel)]="geocoderInput">`
The wiki says nothing aber mgl-feature
component. But being able to set properties is nice to know :)
## mgl-geojson-source [Mapbox GL style spec](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson)
[...]
### Inputs
#### mgl-geojson-source <-
Init only:
* **id**: `string` _(Required)_
Dynamic:
* [**data**](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson-data): `GeoJSON.Feature | GeoJSON.FeatureCollection | string`
* [**maxzoom**](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson-maxzoom): `number`
* [**buffer**](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson-buffer): `number`
* [**tolerance**](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson-tolerance): `number`
* [**cluster**](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson-cluster): `boolean`
* [**clusterRadius**](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson-clusterRadius): `number`
* [**clusterMaxZoom**](https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson-clusterMaxZoom): `number`
#### mgl-feature <-
Init only:
* **geometry**: `GeoJSON.GeometryObject` _(Required)_
* **properties**: `any`
Dynamic:
* **id**: `number`
It's a common use case to pass geometry.coordinates
as lngLat
input.
But when we want to re-open a popup, we need to change the ref of geometry.coordinates
, not just the ref of the feature. It would be better to be able to pass the feature directly (just like it's done in mgl-marker)
as can be seen in examples/ngx-popup-for-layer
click a symbol to open popup. Click another symbol - the popup is removed but not opened again.
Probably this Mapbox issue: mapbox/mapbox-gl-js#2395
Hello,
I'm in a project with Angular 6 and I'm using the library but the map is not displaying and it does not give any errors, is anyone else going through this?
Reproduce:
<mgl-geojson-source id="symbols-source"></mgl-geojson-source>
to a map (doesn't madder if w or w/o <mgl-feature>
.Error: Uncaught (in promise): TypeError: Cannot read property 'unsubscribe' of undefined
TypeError: Cannot read property 'unsubscribe' of undefined
at GeoJSONSourceComponent.ngOnDestroy (geojson-source.component.ts:79)
Please help
base-map.html
<mgl-map #map[style]="'mapbox://styles/mapbox/streets-v9'" [zoom]="[2]" [center]="[-103.59179687498357, 40.66995747013945]" (click)="onMapClick($event, map)"
(load)="mapLoad($event)" (mouseMove)="onMapMouseMove($event, map)" [cursorStyle]="cursorStyle">
<ng-content > </ng-content>
<mgl-control mglGeocoder position="top-right"></mgl-control>
</mgl-map>
base-map.ts
import { Component, } from '@angular/core';
import { MapMouseEvent } from 'mapbox-gl';
@Component({
selector: 'base-map',
templateUrl: 'base-map.component.html',
})
export class BaseMapComponent implements OnChanges {
mapLoad() { }
onMapClick(evt: MapMouseEvent) {
console.log(evt);
}
onMapMouseMove(evt: MapMouseEvent) {
console.log(evt);
}
}
point-map.html
<mgl-geojson-source id="mainSrc_cluster" [data]="mapGeoJSON" [cluster]="isCluster" [clusterMaxZoom]="14" [clusterRadius]="50">
</mgl-geojson-source>
<mgl-layer id="clusters" type="circle" source="mainSrc_cluster" [filter]="['has', 'point_count']" [paint]="{
'circle-color': {
property: 'point_count',
type: 'interval',
stops: [
[0, '#51bbd6'],
[100, '#f1f075'],
[750, '#f28cb1']
]
},
'circle-radius': {
property: 'point_count',
type: 'interval',
stops: [
[0, 20],
[100, 30],
[750, 40]
]
}
}">
</mgl-layer>
<mgl-layer id="cluster-count" type="symbol" source="mainSrc_cluster" [filter]="['has', 'point_count']" [layout]="{
'text-field': '{point_count}',
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12
}">
</mgl-layer>
<mgl-layer id="unclustered-point" type="circle" source="mainSrc_cluster" (click)="onClick($event)" (mouseEnter)="cursorStyle = popupList.length > 0 || pointPopupHeader ? 'pointer' : ''"
(mouseLeave)="cursorStyle = ''" [filter]="['!has', 'point_count']" [paint]="{
'circle-color': unClusterPointColor,
'circle-radius': [
'interpolate', ['linear'], ['zoom'],
10, 5,
22, 20
],
'circle-stroke-width': 1,
'circle-stroke-color': '#fff'
}">
</mgl-layer>
similary i have file for lines.map.html, and have other files, containing layers and event handlers on that layer.
now in the main app file
main-app.ts
<base-map #map>
<point-map [mapData]="mapData" [mapMetaData]="mapMetaData"
[mapComponent]="map.mapComp"
></point-map>
</base-map>
the problem with approach is that im getting error
ERROR Error: StaticInjectorError(AppModule)[LayerComponent -> MapService]:
also the element projected inside ng-content is called first in angular intitalization.
so layer, and geojson component is initialzed before mgl-map component
The latest build seems to have an outdated GeoJSON implementation as I'm getting the following when building today:
ERROR in node_modules/ngx-mapbox-gl/src/app/lib/source/geojson/geojson-source.component.d.ts(6,22): error TS2420: Class 'GeoJSONSourceComponent' incorrectly implements interface 'GeoJSONSourceOptions'.
Types of property 'data' are incompatible.
Type 'string | Feature<GeometryObject, { [name: string]: any; }> | FeatureCollection<GeometryObject, { ...' is not assignable to type 'string | Feature<GeoJSONGeometry, { [name: string]: any; }> | FeatureCollection<GeoJSONGeometry, ...'.
Type 'Feature<GeometryObject, { [name: string]: any; }>' is not assignable to type 'string | Feature<GeoJSONGeometry, { [name: string]: any; }> | FeatureCollection<GeoJSONGeometry, ...'.
Type 'Feature<GeometryObject, { [name: string]: any; }>' is not assignable to type 'Feature<GeoJSONGeometry, { [name: string]: any; }>'.
Type 'GeometryObject' is not assignable to type 'GeoJSONGeometry'.
Type 'GeometryObject' is not assignable to type 'GeometryCollection'.
Types of property 'type' are incompatible.
Type 'GeoJsonGeometryTypes' is not assignable to type '"GeometryCollection"'.
Type '"Point"' is not assignable to type '"GeometryCollection"'.
I'm working on a PR to fix this.
Hey @Wykks using your awesome library again (working on a coffee for you) but came across a strange bug / behaviour.
I use one mgl-geojson-source
and multiple mgl-layer
that filter part of the data (f.e. [filter]="['all', ['!has', 'point_count'], ['==', 'Kategorie', 'Wanderung']]"
) and use an mgl-image
as icon-image
.
So far, so working good. But: every 10th load er so, only some of the layers render initially and only reappear from that on on some zoom-levels (oh and none of them are clickable, if that happens).
You can take a look at the app here: https://tdsn-2018.firebaseapp.com/ (just reload multiple times and suddenly only some of the markers will appear).
Any suggestion / idea / hint would be awesome.
When I load a page with a map on it, everything loads fine and the map shows up and you can do what you will with it. As soon as I leave the page and navigate to another page I get the below error.
ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'remove' of undefined
TypeError: Cannot read property 'remove' of undefined
at n.onRemove (mapbox-gl.js:33)
at o.removeControl (mapbox-gl.js:33)
at ngx-mapbox-gl.js:555
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:388)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:138)
at NgZone.push../node_modules/@angular/core/fesm5/core.js.NgZone.runOutsideAngular (core.js:3783)
at MapService.push../node_modules/ngx-mapbox-gl/fesm5/ngx-mapbox-gl.js.MapService.removeControl (ngx-mapbox-gl.js:554)
at ControlComponent.push../node_modules/ngx-mapbox-gl/fesm5/ngx-mapbox-gl.js.ControlComponent.ngOnDestroy (ngx-mapbox-gl.js:1222)
at callProviderLifecycles (core.js:9573)
at callElementProvidersLifecycles (core.js:9541)
at n.onRemove (mapbox-gl.js:33)
at o.removeControl (mapbox-gl.js:33)
at ngx-mapbox-gl.js:555
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:388)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.run (zone.js:138)
at NgZone.push../node_modules/@angular/core/fesm5/core.js.NgZone.runOutsideAngular (core.js:3783)
at MapService.push../node_modules/ngx-mapbox-gl/fesm5/ngx-mapbox-gl.js.MapService.removeControl (ngx-mapbox-gl.js:554)
at ControlComponent.push../node_modules/ngx-mapbox-gl/fesm5/ngx-mapbox-gl.js.ControlComponent.ngOnDestroy (ngx-mapbox-gl.js:1222)
at callProviderLifecycles (core.js:9573)
at callElementProvidersLifecycles (core.js:9541)
at resolvePromise (zone.js:814)
at resolvePromise (zone.js:771)
at zone.js:873
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:3815)
at ZoneDelegate.push../node_modules/zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:420)
at Zone.push../node_modules/zone.js/dist/zone.js.Zone.runTask (zone.js:188)
at drainMicroTaskQueue (zone.js:595)
at ZoneTask.push../node_modules/zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (zone.js:500)
at invokeTask (zone.js:1540)
package.json
{
"name": "ncri-spa",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"dev": "ng build",
"production": "ng build --configuration=production",
"staging": "ng build --configuration=staging",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@amcharts/amcharts3-angular": "^1.5.0",
"@angular/animations": "^6.0.0",
"@angular/common": "^6.0.0",
"@angular/compiler": "^6.0.0",
"@angular/core": "^6.0.0",
"@angular/forms": "^6.0.0",
"@angular/http": "^6.0.0",
"@angular/platform-browser": "^6.0.0",
"@angular/platform-browser-dynamic": "^6.0.0",
"@angular/router": "^6.0.0",
"@aspnet/signalr": "^1.0.2",
"@auth0/angular-jwt": "^2.0.0",
"@mapbox/mapbox-gl-draw": "^1.0.9",
"@mapbox/mapbox-gl-geocoder": "^2.3.0",
"@mapbox/mapbox-gl-sync-move": "^0.2.0",
"core-js": "^2.5.4",
"jwt-decode": "^2.2.0",
"mapbox-gl": "^0.47.0",
"mapbox-gl-compare": "^0.2.0",
"ngx-mapbox-gl": "^2.0.0",
"primeng": "^6.1.0",
"rxjs": "^6.0.0",
"zone.js": "^0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.6.1",
"@angular/cli": "~6.0.1",
"@angular/compiler-cli": "^6.0.0",
"@angular/language-service": "^6.0.0",
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/jwt-decode": "^2.2.1",
"@types/mapbox-gl": "^0.47.0",
"@types/node": "~8.9.4",
"codelyzer": "~4.2.1",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~1.7.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~1.4.2",
"karma-jasmine": "~1.1.1",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.3.0",
"ts-node": "~5.0.1",
"tslint": "~5.9.1",
"typescript": "~2.7.2"
},
"browser": {
"fs": false,
"path": false,
"os": false
}
}
app.module.ts
NgxMapboxGLModule.withConfig({
// Can also be set per map (accessToken input of mgl-map)
accessToken: 'my access token',
// Optionnal, specify if different from the map access token, can also be set per mgl-geocoder (accessToken input of mgl-geocoder)
// geocoderAccessToken: 'TOKEN'
}),
map.component.html
<div style="width: 100%; height: 400px;">
<mgl-map #mbmap [style]="'mapbox://styles/mapbox/satellite-v9?optimize=true'" [zoom]="[3]" [center]="[-113.517209517767,50.950570]"
(load)="onLoad($event)" [attributionControl]="false">
<mgl-control mglNavigation></mgl-control>
<mgl-control mglFullscreen position="top-left"></mgl-control>
<mgl-control mglGeolocate position="top-left"></mgl-control>
<mgl-control mglScale unit="metric" position="bottom-right"></mgl-control>
<mgl-control position="bottom-left">
<div class='legend' [style.display]="max === 0 ? 'none' : 'block'">
<h4>Legend</h4>
<div class="legend-content">
<div [ngClass]="selectedGradient.class" class="legend-scale"></div>
<div class="legend-values">
<h5 *ngFor="let s of selectedGradient.colors; let i = index">{{(min + (i * ((max - min) / selectedGradient.colors.length))).toFixed(2)}}</h5>
<h5>{{max.toFixed(2)}}</h5>
</div>
</div>
</div>
</mgl-control>
</mgl-map>
</div>
map.component.ts
import { HttpClient } from '@angular/common/http';
import { AppService } from '../../app.service';
import { Component, OnInit, Input, Output, OnChanges, SimpleChanges, EventEmitter } from '@angular/core';
import { } from 'mapbox-gl';
import { SharedComponent } from '../shared.component';
import { Map } from 'mapbox-gl';
@Component({
selector: 'app-mb-map',
templateUrl: './mb-map.component.html',
styleUrls: ['./mb-map.component.scss']
})
export class MbMapComponent extends SharedComponent implements OnInit, OnChanges {
// demo's of things you can do
// https://wykks.github.io/ngx-mapbox-gl/demo/edit/live-update-feature
/**
* THe layer id to remove and add to the map when changing data layers.
*/
layerId: number;
/**
* The source id to remove and add to the map when changing data layers.
*/
sourceId: number;
/**
* The mapbox map.
*/
map: Map;
/**
* The max value of the dataset.
*/
max: number;
/**
* The min value of the dataset.
*/
min: number;
/**
* The color gradients to apply to the map data with class names for legend.
*/
gradients: any[];
/**
* The selected gradient chosen to render.
*/
selectedGradient: any;
/**
* The points passed from a parent component.
*/
@Input('data') data: any;
// test files
testFile: string;
constructor(public appService: AppService, public httpClient: HttpClient) {
super(appService);
this.busy = false;
this.gradients = [
{ class: 'r2g', colors: ['rgba(238, 46, 47, 0.3)', 'rgba(254, 204, 47, 0.3)', 'rgba(40, 167, 69, 0.3)'] },
{ class: 'b2p', colors: ['rgba(22, 103, 225, 0.3)', 'rgba(98, 22, 225, 0.3)', 'rgba(225, 22, 213, 0.3)'] },
];
this.selectedGradient = this.gradients[0];
this.layerId = 0;
this.sourceId = 0;
this.min = 0;
this.max = 0;
}
/**
* Update map on changes
* @param changes
*/
ngOnChanges(changes: SimpleChanges): void {
if (changes.data && changes.data.currentValue !== changes.data.previousValue) {
if (this.data.points && this.data.points.features.length > 0) {
// set the min and max for the legend
this.min = this.data.min;
this.max = this.data.max;
// draw the marker on the map
// this.drawMarker(this.data.marker, this.data.points.features[0].geometry.coordinates);
// TODO: Add the layer name to the data that gets passed to the points as the layer id and source id instead of incrementing.
// Might be able to save multiple layers in the map once loaded
// so we can hide or show them without reloading the data.
// draw the geojson layer
this.drawGeoJsonPoints(
this.map,
this.data.points,
`sourceId-${this.sourceId}`,
`layerId-${this.layerId}`,
this.data.min,
this.data.max,
this.selectedGradient.colors
);
}
}
}
ngOnInit() {
}
drawMarker(title: string, coordinates: number[]) {
// check if marker exists
const exists = this.map.getLayer('marker');
// remove if it does
if (exists) {
this.map.removeLayer('marker');
}
// add new marker
this.map.addLayer({
'id': 'marker',
'type': 'symbol',
'source': {
'type': 'geojson',
'data': {
'type': 'FeatureCollection',
'features': [{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': coordinates
},
'properties': {
'title': title,
'icon': 'monument'
}
}]
}
},
'layout': {
'icon-image': '{icon}-15',
'text-field': '{title}',
'text-font': ['Open Sans Semibold', 'Arial Unicode MS Bold'],
'text-offset': [0, 0.6],
'text-anchor': 'top'
}
});
}
/**
* Add's a layer on the map load function.
* @param map The map.
*/
onLoad(map: Map) {
this.map = map;
}
/**
* Draw's a set of GeoJson points on the map.
* @param map The map to draw on.
* @param geojson The path to a GeoJson file or a string representing GeoJson data.
* @param sourceId An arbitrary unique id that separates this layer from any other added layers.
* @param min The minimum value of the data source.
* @param max The maximum value of the data source.
* @param rgbaGradient The color gradient to use.
*/
drawGeoJsonPoints(map: Map, geojson: any, sourceId: string, layerId: string, min: number, max: number, rgbaGradient: string[]) {
// check for existence of layer
const layerExists = map.getLayer(layerId);
// remove it if it does
if (layerExists) {
map.removeLayer(layerId);
console.log(`Layer with id of ${layerId} exists. Removing ${layerId}.`);
}
// check if source exists
const sourceExists = map.getSource(sourceId);
// remove it if it does
if (sourceExists) {
map.removeSource(sourceId);
console.log(`Source with id of ${sourceId} exists. Removing ${sourceId}.`);
}
// add a data source for the map data to load from
console.log('Adding source to map...');
map.addSource(sourceId, {
type: 'geojson',
data: geojson, // path to geojson file
cluster: true, // enable clustering
clusterMaxZoom: 6, // Max zoom to cluster points on
// clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)
});
// get zone ranges for paint gradient
console.log('Calculating zone...');
const zone = (max - min) / rgbaGradient.length;
// calculate stop values for each color gradient
console.log('Calculating stop values for gradients from zone...');
const gradients = [];
for (let i = 0; i < rgbaGradient.length; i++) {
gradients.push(min + (i * zone));
gradients.push(rgbaGradient[i]);
}
// declare paint object prior
// add interpolate function
// interpolation type
// the function to retrieve the value to interpolate by
// the list of gradients and their stop values
console.log('Configuring paint interpolation...');
const paint = {
'circle-color': ['interpolate', ['linear'], ['get', 'value'], ...gradients]
};
console.log('Adding layer...');
map.addLayer({
id: layerId,
type: 'circle',
source: sourceId,
// filter: ['has', 'value'], // checks for nulls, if any are present it doesn't draw anything at all
paint: paint,
});
console.log('Map points loaded.');
}
}
Any chance anyone else is experiencing this problem?
Hi, first of all thanks for great library!
I noticed that I get some unwanted events fired for the popup (close)
event. I believe that is because you are removing and adding the popup again to the map with the updated lnglat value. Could it be possible to use the underlying popup's setLngLat
function instead? this should also update the map without firing the close events, I believe.
On a second note, have you considered adding mapbox gl draw library bindings? I might be able to help with that as well as I'm doing something with the draw library right now.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.