UNPKG

28.6 kBMarkdownView Raw
1# @asymmetrik/ngx-leaflet
2
3[![Build Status][travis-image]][travis-url]
4
5[travis-url]: https://travis-ci.org/Asymmetrik/ngx-leaflet/
6[travis-image]: https://travis-ci.org/Asymmetrik/ngx-leaflet.svg?branch=master
7
8> Leaflet packages for Angular.io.
9> Provides flexible and extensible components for integrating Leaflet v0.7.x and v1.x into Angular.io projects.
10> Supports Angular v18 and use in Angular-CLI based projects.
11
12> NOTE: This is the last version of this package that will be published under the @asymmetrik namespace.
13> This and future versions will be published under the namespace: @bluehalo
14
15## Table of Contents
16- [Install](#install)
17- [Usage](#usage)
18- [API](#api)
19- [Extensions](#extensions)
20- [Getting Help](#help)
21- [Contribute](#contribute)
22- [License](#license)
23- [Credits](#credits)
24
25
26
27## Install
28Install the package and its peer dependencies via npm (or yarn):
29```
30npm install leaflet
31npm install @asymmetrik/ngx-leaflet
32```
33
34If you intend to use this library in a typescript project (utilizing the typings), you'll need to install the leaflet typings:
35```
36npm install --save-dev @types/leaflet
37```
38
39If you want to run the demo, clone the repository, perform an ```npm install```, ```npm run demo``` and then go to http://localhost:4200
40
41Not using the latest version of Angular.io? Have a look in [CHANGES.md](/CHANGES.md) to find the right version for your project.
42
43## Usage
44To use this library, there are a handful of setup steps to go through that vary based on your app environment (e.g., Webpack, ngCli, SystemJS, etc.).
45Generally, the steps are:
46
47* Install Leaflet, this library, and potentially the Leaflet typings (see above).
48* Import the Leaflet stylesheet
49* Import the Leaflet module into your Angular project
50* Create and configure a map (see docs below and/or demo)
51
52
53### Import the Leaflet Stylesheet
54For leaflet to work, you need to have the leaflet stylesheets loaded into your application.
55If you've installed via npm, you will need to load ```./node_modules/leaflet/dist/leaflet.css```.
56How you include the stylesheet will depend on your specific setup. Here are a few examples:
57
58#### Direct Import from HTML
59If you are just building a webpage and not using a bundler for your css, you'll want to directly import the css file in your HTML page.
60
61```html
62<head>
63 ...
64 <link rel="stylesheet" type="text/css" href="./node_modules/leaflet/dist/leaflet.css">
65 ...
66</head>
67```
68
69#### Configuring Webpack Style Loaders
70If you are using Webpack, you will need to import the css file and have a style-loader configured.
71You can use the demo included in this application as a reference.
72
73Generally, in ```vendor.ts```:
74```ts
75import 'leaflet/dist/leaflet.css';
76```
77
78And then in your webpack config file:
79```js
80{
81 ...
82 "module" : {
83 loaders: [
84 ...
85 { test: /\.css$/, loaders: [ 'style-loader', 'css-loader' ] },
86 ...
87 ]
88 },
89 ...
90}
91```
92
93
94#### Adding Styles in Angular CLI
95If you are using Angular CLI, you will need to add the Leaflet CSS file to the styles array contained in ```angular.json```
96
97```json
98{
99 ...
100 "styles": [
101 "styles.css",
102 "./node_modules/leaflet/dist/leaflet.css"
103 ],
104 ...
105}
106```
107
108### Import Code Dependencies and Module
109This project is exported using UMD and it includes typings.
110So, you shouldn't have to do anything special to use it if you're building your project in Typescript.
111
112#### Typescript Angular.io Module Import
113Before you can use the module in your Angular.io app, you'll need to import it in your application (and potentially the module that's using it).
114
115For example, in your ```app.module.ts```, add:
116
117```js
118import { LeafletModule } from '@asymmetrik/ngx-leaflet';
119
120...
121imports: [
122 ...
123 LeafletModule
124]
125...
126
127```
128
129Potentially, you'll also need to import it into the module of the component that is going to actually use the ngx-leaflet directives.
130See Angular.io docs of modules for more details (https://angular.io/guide/ngmodule). In this case, in ```my-module.module.ts```, add:
131
132```js
133import { LeafletModule } from '@asymmetrik/ngx-leaflet';
134
135...
136imports: [
137 ...
138 LeafletModule
139]
140...
141
142```
143
144
145#### Not Using Typescript?
146You brave soul.
147The code is exported using UMD.
148The bundles are generated as part of the build (`npm run build`) and placed into the ./dist dir.
149You should be able to import is using whatever module system/builder you're using, even if you aren't using Typescript.
150
151
152### Create and Configure a Map
153Once the dependencies are installed and you have imported the ```LeafletModule```, you're ready to add a map to your page.
154To get a basic map to work, you have to:
155
156* Apply the ```leaflet``` attribute directive (see the example below) to an existing DOM element.
157* Style the map DOM element with a height. Otherwise, it'll render with a 0 pixel height.
158* Provide an initial zoom/center and set of layers either via ```leafletOptions``` or by binding to ```leafletZoom```, ```leafletCenter```, and ```leafletLayers```.
159
160Template:
161```html
162<div style="height: 300px;"
163 leaflet
164 [leafletOptions]="options">
165</div>
166```
167
168Example leafletOptions object:
169```js
170options = {
171 layers: [
172 tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '...' })
173 ],
174 zoom: 5,
175 center: latLng(46.879966, -121.726909)
176};
177```
178
179Changes to leafletOptions are ignored after they are initially set.
180This is because these options are passed into the map constructor, so they can't be changed anyways.
181So, make sure the object exists before the map is created.
182You'll want to create the object in ```ngOnInit``` or hide the map DOM element with ```*ngIf``` until you can create the options object.
183
184
185### Add a Layers Control
186The ```[leafletLayersControl]``` input bindings give you the ability to add the layers control to the map.
187The layers control lets the user toggle layers and overlays on and off.
188
189Template:
190```html
191<div style="height: 300px;"
192 leaflet
193 [leafletOptions]="options"
194 [leafletLayersControl]="layersControl">
195</div>
196```
197
198Example layersControl object:
199```js
200layersControl = {
201 baseLayers: {
202 'Open Street Map': tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '...' }),
203 'Open Cycle Map': tileLayer('https://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '...' })
204 },
205 overlays: {
206 'Big Circle': circle([ 46.95, -122 ], { radius: 5000 }),
207 'Big Square': polygon([[ 46.8, -121.55 ], [ 46.9, -121.55 ], [ 46.9, -121.7 ], [ 46.8, -121.7 ]])
208 }
209}
210```
211
212You can add any kind of Leaflet layer you want to the ```overlays``` map.
213This includes markers, shapes, geojson, custom layers from other libraries, etc.
214
215
216### Add Custom Layers (base layers, markers, shapes, etc.)
217There are several different ways to add layers to the map.
218You can add layers (baselayers, markers, or custom layers) to the map without showing them in the layer control using the ```[leafletLayers]``` directive.
219
220Template:
221```html
222<div style="height: 300px;"
223 leaflet
224 [leafletOptions]="options"
225 [leafletLayers]="layers">
226</div>
227```
228
229Layers array:
230```js
231layers = [
232 circle([ 46.95, -122 ], { radius: 5000 }),
233 polygon([[ 46.8, -121.85 ], [ 46.92, -121.92 ], [ 46.87, -121.8 ]]),
234 marker([ 46.879966, -121.726909 ])
235];
236```
237
238You can also add an individual layer to the map using the ```[leafletLayer]``` directive.
239Using this approach allows you to use ```*ngFor``` and ```*ngIf``` to control whether individual layers are added to or removed from the map.
240
241Template:
242```html
243<div style="height: 300px;"
244 leaflet
245 [leafletOptions]="options">
246 <div *ngIf="showLayer" [leafletLayer]="layer"></div>
247</div>
248```
249
250Layer:
251```js
252layer = circle([ 46.95, -122 ], { radius: 5000 });
253```
254
255
256### Dynamically Change Map Layers using [leafletLayers]
257
258> **Layer inputs (arrays and maps) are mutable**
259> Previous versions of this plugin treated layers arrays and layer control objects as immutable data structures.
260> We've changed that behavior.
261> Now, mutable changes to the ```leafletLayers```, ```leafletBaseLayers```, and ```leafletLayersControl``` inputs are detected.
262
263The plugin is now using internal ngx iterable and key/value differs to detect and track changes to mutable data structures.
264This approach requires a deep compare of the contents of the data structure (which can be slow when the contents are really big).
265For immutable data structures, all that is needed is a top-level instance equality check (which is way faster).
266This change is backwards compatible and was motivated by feedback and confusion.
267While there is a performance impact for some use cases, this approach is more intuitive.
268
269There are at least two good approaches to improving performance when there are a lot of layers bound to the map.
270First, you can use the OnPush change detection strategy. There's an example of this in the demo.
271Second, you can wrap a large number of layers into a Leaflet layer group, which will reduce the number of layers the plugin actually has to track during diffs.
272
273
274### Working with Leaflet Events
275Often, you'll want to make changes based on a map click or other Leaflet interaction.
276The ngx-leaflet plugin supports several [map events](#map-events) and [layer events](#layer-events) as documented in the API section.
277
278You may occasionally need to handle events that aren't exposed through the plugin, however.
279When that happens, you will need to be aware of how Zones and change detection work to ensure your event handling works as expected.
280Take a look at [A Note About Change Detection](#a-note-about-change-detection) for more details.
281This is by design and a common thing to deal with when using third party libraries and Angular.
282
283
284## API
285This section includes more detailed documentation of the functionality of the directives included in this library.
286
287### Advanced Map Configuration
288There are several input bindings available for configuring the map.
289
290```html
291<div leaflet style="height: 300px;"
292 [leafletOptions]="options"
293 [leafletPanOptions]="panOptions"
294 [leafletZoomOptions]="zoomOptions"
295 [leafletZoomPanOptions]="zoomPanOptions"
296 [leafletFitBoundsOptions]="fitBoundsOptions">
297</div>
298```
299
300#### [leafletOptions]
301Input binding for the initial leaflet map options (see [Leaflet's](https://leafletjs.com/SlavaUkraini/reference.html#map-option) docs). These options can only be set initially because they are used to create the map. Later changes are ignored.
302
303#### [leafletPanOptions]
304Input binding for pan options (see [Leaflet's](https://leafletjs.com/SlavaUkraini/reference.html#pan-options) docs). These options are stored and used whenever pan operations are invoked.
305
306#### [leafletZoomOptions]
307Input binding for zoom options (see [Leaflet's](https://leafletjs.com/SlavaUkraini/reference.html#zoom-options) docs). These options are stored and used whenever zoom operations are invoked.
308
309#### [leafletZoomPanOptions]
310Input binding for zoom/pan options (see [Leaflet's](https://leafletjs.com/SlavaUkraini/reference.html#zoom/pan-options) docs). These options are stored and used whenever zoom/pan operations are invoked.
311
312#### [leafletFitBoundsOptions]
313Input binding for FitBounds options (see [Leaflet's](https://leafletjs.com/SlavaUkraini/reference.html#fitbounds-options) docs). These options are stored and used whenever FitBounds operations are invoked.
314
315
316### Dynamically changing zoom level, center, fitBounds, etc.
317```html
318<div leaflet style="height: 300px;"
319 [leafletOptions]="options"
320 [(leafletZoom)]="zoom"
321 [(leafletCenter)]="center"
322 [leafletFitBounds]="fitBounds">
323</div>
324```
325
326#### [(leafletZoom)]: number
327Input and Output binding for the map zoom level.
328
329#### [leafletMaxZoom]: number
330Input binding for the maximum zoom level for the map.
331
332#### [leafletMinZoom]: number
333Input binding for the minimum zoom level for the map.
334
335#### [(leafletCenter)]: LatLng
336Input and Output binding for the map center position.
337
338#### Note: center/zoom operations may interfere with each other
339Zoom/Center operations that are applied in rapid succession may interfere with or cancel each other.
340If both changes are picked up at the same time, the component applies the changes as a map.setView() operation to ensure both are processed.
341Additionally, if a zoom level or center is applied that is not allowed (e.g., beyond max zoom level or outside of max bounds), Leaflet will determine the new value.
342
343#### [leafletFitBounds]: LatLngBounds
344Input bind a ```LatLngBounds``` value that will be applied to the map using ```Map.setFitBounds()```.
345This operation has no output binding because the input fitBounds usually results in a slightly different map bounds.
346
347#### [leafletMaxBounds]: LatLngBounds
348Input bind a ```LatLngBounds``` value that will be applied to the map using ```Map.setMaxBounds()```.
349
350
351### Simple Layer Management: Setting Baselayers
352There is a convenience input binding for setting the baselayers on the map called ```[leafletBaseLayers]```.
353You can also provide ```[leafletLayersControlOptions]``` if you want to show the control on the map that allows you to switch between baselayers.
354If you plan to show more than just baselayers, you should use the more advanced layers controls described in *Advanced Layer Management* below.
355
356For an example of the basic map setup, you should check out the *Simple Base Layers* demo.
357
358```html
359<div leaflet style="height: 300px;"
360 [leafletOptions]="options"
361 [leafletBaseLayers]="baseLayers"
362 [leafletLayersControlOptions]="layersControlOptions">
363</div>
364```
365
366#### [leafletBaseLayers]: Control.LayersObject
367Input bind an ```Control.LayersObject``` to be synced to the map.
368
369```js
370baseLayers: {
371 'layer1': Layer,
372 'layer2': Layer
373}
374```
375
376On changes, the component syncs the baseLayers on the map with the layers in this object.
377Syncing is performed by tracking the current baselayer and on changes, searching the map to see if any of the current baselayers is added to the map.
378If it finds a baselayer that is still added to the map, it will assume that is still the baselayer and leave it.
379If none of the baselayers can be found on the map, it will add the first layer it finds in the ```Control.LayersObject``` and use that as the new baselayer.
380Layers are compared using instance equality.
381
382If you use this directive, you can still manually use the ```[leafletLayers]``` directive, but you will not be able to use the ```[leafletLayersControl]``` directive.
383This directive internally uses the layers control, so if you add both, they'll interfere with each other.
384Because it uses ```control.layers``` under the hood, you can still provide options for the layers control.
385
386
387#### [leafletLayersControlOptions]
388Input binding for Control.Layers options (see [Leaflet's](https://leafletjs.com/SlavaUkraini) docs).
389These options are passed into the layers control constructor on creation.
390
391
392### Advanced Layer Management: Layers, and Layers Control
393The ```[leafletLayers]``` and ```[leafletLayersControl]``` input bindings give you direct access to manipulate layers and the layers control.
394When the array bound to ```[leafletLayers]``` is changed, the directive will synchronize the layers on the map to the layers in the array.
395This includes tile layers and any added shapes.
396
397The ```[leafletLayersControl]``` input binding allows you to provide a set of base layers and overlay layers that can be managed within leaflet using the layers control.
398When the user manipulates the control via Leaflet, Leaflet will automatically manage the layers, but the input bound layer array isn't going to get updated to reflect those changes.
399
400So, use ```[leafletLayers]``` to add a collection of layers to the map.
401And, use ```[leafletLayersControl]``` to allow users to optionally turn layers/overlays on and off.
402
403For an example of using the layers controls, you should check out the *Layers and Layer Controls* demo.
404
405```html
406<div leaflet style="height: 300px;"
407 [leafletOptions]="options"
408 [leafletLayers]="layers"
409 [leafletLayersControl]="layersControl"
410 [leafletLayersControlOptions]="layersControlOptions">
411</div>
412```
413
414#### [leafletLayers]: Layer[]
415Input bind an array of all layers to be synced (and made visible) in the map.
416
417On changes, the component syncs the layers on the map with the layers in this array.
418Syncing is performed by selectively adding or removing layers.
419Layers are compared using instance equality.
420As a result of how the map is synced, the order of layers is not guaranteed to be consistent as changes are made.
421
422
423#### [leafletLayersControl]: Control.Layers
424Input bind a Control.Layers specification. The object contains properties for each of the two constructor arguments for the Control.Layers constructor.
425
426```js
427layersControl: {
428 baseLayers: {
429 'layerName': Layer
430 },
431 overlays: {
432 'overlayName': Layer
433 }
434}
435```
436
437#### [leafletLayersControlOptions]
438Input binding for Control.Layers options (see [Leaflet's](https://leafletjs.com/SlavaUkraini) docs).
439These options are passed into the constructor on creation.
440
441
442### Advanced Layer Management: Individual Layers and *ngFor / *ngIf
443The ```[leafletLayer]``` input bindings gives you the ability to add a single layer to the map.
444While this may seem limiting, you can nest elements inside the map element, each with a ```[leafletLayer]``` input.
445The result of this is that each layer will be added to the map.
446If you add a structural directive - ```*ngFor``` or ```*ngIf``` - you can get some added flexibility when controlling layers.
447
448```html
449<div leaflet style="height: 300px;"
450 [leafletOptions]="options">
451 <div *ngFor="let l of layers" [leafletLayer]="l"></div>
452</div>
453```
454
455In this example, each layer in the ```layers``` array will create a new child ```div``` element.
456Each element will have a ```[leafletLayer]``` input binding, which will result in the layer being added to the map.
457For more details, you should check out the *Layers and ngFor* demo.
458
459There are several layer events that are available when you are using this approach to controlling layers.
460
461### Layer Events
462When you are using the ```[leafletLayer]``` directive to add a layer, you can also access output bindings for layer events.
463Two events that are currently exposed include: ```(leafletLayerAdd)``` and ```(leafletLayerRemove)```.
464Each of these emits a ```LeafletEvent``` object.
465
466
467### Map Events
468Leaflet exposes a lot of map events including map zoom, map move, and mouse interactions.
469The plugin exposes several of the most common events.
470For each of these events, the event is emitted in the Angular Zone, so you shouldn't have to do anything extra to get change detection to work.
471For a working example, check out the events section of the demo.
472
473#### Mouse Interactions: LeafletMouseEvent
474The following events are provided:
475* ```(leafletClick)```
476* ```(leafletDoubleClick)```
477* ```(leafletMouseDown)```
478* ```(leafletMouseUp)```
479* ```(leafletMouseMove)```
480* ```(leafletMouseOver)```
481* ```(leafletMouseOut)```
482
483#### Map Zoom and Move: LeafletEvent
484The following events are provided:
485* ```(leafletMapMove)```
486* ```(leafletMapMoveStart)```
487* ```(leafletMapMoveEnd)```
488* ```(leafletMapZoom)```
489* ```(leafletMapZoomStart)```
490* ```(leafletMapZoomEnd)```
491
492
493
494### Getting a Reference to the Map
495Occasionally, you may need to directly access the Leaflet map instance.
496For example, to call ```invalidateSize()``` when the map div changes size or is shown/hidden.
497There are a couple of different ways to achieve this depending on what you're trying to do.
498
499The easiest and most flexible way is to use the output binding ```leafletMapReady```.
500This output is invoked after the map is created, the argument of the event being the ```Map``` instance.
501
502The second is to get a reference to the leaflet directive itself - and there are a couple of ways to do this.
503With a reference to the directive, you can invoke the ```getMap()``` function to get a reference to the ```Map``` instance.
504
505
506#### (leafletMapReady): Map
507This output is emitted when once when the map is initially created inside of the Leaflet directive.
508The event will only fire when the map exists and is ready for manipulation.
509
510```html
511<div leaflet
512 [leafletOptions]="options"
513 (leafletMapReady)="onMapReady($event)">
514</div>
515```
516
517```js
518onMapReady(map: Map) {
519 // Do stuff with map
520}
521```
522
523This method of getting the map makes the most sense if you are using the Leaflet directive inside your own component
524and just need to add some limited functionality or register some event handlers.
525
526
527#### Inject LeafletDirective into your Component
528This is the more advanced technique and it won't always work depending on your setup.
529In particular, this will likely not work unless you are writing your own third-party library that extends the functionality of `ngx-leaflet`.
530If this approach does not work for you, try using the `leafletMapReady` event described above.
531
532In Angular.io, directives are injectable the same way that Services are.
533This means that you can create your own component or directive and inject the ```LeafletDirective``` into it.
534This will only work if your custom component/directive exists on the same DOM element and is ordered after the injected LeafletDirective, or if it is on a child DOM element.
535
536
537```html
538<!-- On the same DOM element -->
539<div leaflet myCustomDirective>
540</div>
541
542<!-- On a child DOM element -->
543<div leaflet>
544 <div myCustomDirective></div>
545</div>
546```
547
548```js
549@Directive({
550 selector: '[myCustomDirective]'
551})
552export class MyCustomDirective {
553 leafletDirective: LeafletDirective;
554
555 constructor(leafletDirective: LeafletDirective) {
556 this.leafletDirective = leafletDirective;
557 }
558
559 someFunction() {
560 if (null != this.leafletDirective.getMap()) {
561 // Do stuff with the map
562 }
563 }
564}
565```
566
567The benefit of this approach is it's a bit cleaner if you're interested in adding some reusable capability to the existing leaflet map directive.
568As mentioned above, it might not work depending on how you are packaging your component.
569This is how the ```@asymmetrik/ngx-leaflet-draw``` and ```@asymmetrik/ngx-leaflet-d3``` packages work, so you can use them as references.
570
571
572### A Note About Change Detection
573Change detection is at the core of how Angular works.
574Angular.io uses Zone.js to scope how and when (events, actions, etc.) to trigger change detection.
575It's important to scope it carefully because change detection can be fairly expensive, so you don't want it to happen constantly.
576
577Libraries like ngx-leaflet have to decide what to do inside and outside of the Angular zone, balancing convenience and performance.
578Leaflet registers handlers for a lot of mouse events.
579To mitigate the performance impact of constantly running change detection on all mouse events (including mousemove), ngx-leaflet runs most of the Leaflet code outside of the Angular zone.
580The impact of this is that Angular won't automatically detect changes that you make inside of a Leaflet event callback.
581
582The solution is to either make sure that Angular relevant changes are made inside of Angular's zone or to manually tell Angular to detect changes.
583
584#### Running Inside of Angular's Zone
585Leaflet event handlers run outside of Angular's zone, where changes to input bound fields will not be detected automatically.
586To ensure your changes are detected and applied, you need to make those changed inside of Angular's zone.
587Fortunately, this is extremely easy.
588
589```js
590fitBounds: any = null;
591circle = circle([ 46.95, -122 ], { radius: 5000 });
592
593// Inject the Change Detector into your component
594constructor(private zone: NgZone) {}
595
596ngOnInit() {
597
598 // The 'add' event callback handler happens outside of the Angular zone
599 this.circle.on('add', () => {
600
601 // But, we can run stuff inside of Angular's zone by calling NgZone.run()
602 // everything inside the arrow function body happens inside of Angular's zone, where changes will be detected
603 this.zone.run(() => {
604 this.fitBounds = this.circle.getBounds();
605 });
606
607 });
608}
609```
610
611#### Manually Triggering Change Detection
612Another option is to manually tell the change detector to detect changes.
613The drawback to this option is that it is less precise.
614This will trigger change detection for this component and all of its children.
615
616```js
617fitBounds: any = null;
618circle = circle([ 46.95, -122 ], { radius: 5000 });
619
620// Inject the Change Detector into your component
621constructor(private changeDetector: ChangeDetectorRef) {}
622
623ngOnInit() {
624
625 // The 'add' event callback happens outside of the Angular zone
626 this.circle.on('add', () => {
627
628 // Because we're outside of Angular's zone, this change won't be detected
629 this.fitBounds = this.circle.getBounds();
630
631 // But, it will if we tell Angular to detect changes
632 this.changeDetector.detectChanges();
633
634 });
635}
636```
637
638
639### A Note About Markers
640If you use this component in an Angular.io project and your project uses a bundler like Webpack, you might run into issues using Markers on maps.
641The issue is related to how Leaflet manipulates the image URLs used to render markers when you are using the default marker images.
642The url manipulation is done at runtime and it alters the URLs in a way that breaks their format (this happens regardless of if you're using a file-loader or a url-loader).
643The demo contained in this project demonstrates how to get around this problem (at least in a Webpack environment).
644But, here is a rough overview of the steps taken to get them working.
645
646#### Webpack Marker Workaround
647
6481. Import the marker images in your vendor file to get Webpack to process the images in the asset pipeline
649
650 ```js
651 import 'leaflet/dist/images/marker-shadow.png';
652 import 'leaflet/dist/images/marker-icon.png';
653 ```
654
6551. Either host the images statically or use the file-loader Webpack plugin to generate the images.
6561. Determine the correct URL for the marker and marker-shadow images. If you're using a file hasher, you should be able to check Webpack's output for the generated images. If you are serving them directly without chunk hashing just figure out how to resolve the images on your server.
6571. Configure Leaflet to use the correct URLs as customer marker images
658
659 ```js
660 let layer = marker([ 46.879966, -121.726909 ], {
661 icon: icon({
662 ...Icon.Default.prototype.options,
663 iconUrl: '2b3e1faf89f94a4835397e7a43b4f77d.png',
664 iconRetinaUrl: '680f69f3c2e6b90c1812a813edf67fd7.png',
665 shadowUrl: 'a0c6cc1401c107b501efee6477816891.png'
666 })
667 });
668 ```
669
670#### Angular CLI Marker Workaround
671
672If you build your project using the [Angular CLI](https://github.com/angular/angular-cli), you can make the default leaflet marker assets available by doing the following:
673
6741. Configure the CLI (by editing `angular.json`)to include leaflet assets as below. Detailed instructions can be found in the [asset-configuration](https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/asset-configuration.md) documentation.
675 ```json
676 {
677 ...
678 "assets": [
679 "assets",
680 "favicon.ico",
681 {
682 "glob": "**/*",
683 "input": "./node_modules/leaflet/dist/images",
684 "output": "assets/"
685 }
686 ],
687 ...
688 }
689 ```
690
6911. Configure Leaflet to use the correct URLs as customer marker images
692
693 ```js
694 let layer = marker([ 46.879966, -121.726909 ], {
695 icon: icon({
696 ...Icon.Default.prototype.options,
697 iconUrl: 'assets/marker-icon.png',
698 iconRetinaUrl: 'assets/marker-icon-2x.png',
699 shadowUrl: 'assets/marker-shadow.png'
700 })
701 });
702 ```
703
704## Extensions
705There are several libraries that extend the core functionality of ngx-leaflet:
706* [Leaflet Draw](https://github.com/BlueHalo/ngx-leaflet-draw)
707* [Leaflet Markercluster](https://github.com/BlueHalo/ngx-leaflet-markercluster)
708* [Leaflet D3 (Hexbins)](https://github.com/BlueHalo/ngx-leaflet-d3)
709
710
711## <a name="help">Getting Help</a>
712Here's a list of articles, tutorials, guides, and help resources:
713* [ngx-leaflet on Stack Overflow](https://stackoverflow.com/questions/tagged/ngx-leaflet)
714* [High-level intro to @asymmetrik/ngx-leaflet](https://github.com/BlueHalo/ngx-leaflet/wiki)
715* [Using @asymmetrik/ngx-leaflet in Angular CLI projects](https://github.com/BlueHalo/ngx-leaflet/wiki/Getting-Started-Tutorial)
716* [Integrating 3rd Party Leaflet Libraries with @asymmetrik/ngx-leaflet and @angular/cli](https://github.com/BlueHalo/ngx-leaflet/wiki/Integrating-Plugins)
717
718
719## Contribute
720PRs accepted. If you are part of BlueHalo, please make contributions on feature branches off of the ```develop``` branch. If you are outside of BlueHalo, please fork our repo to make contributions.
721
722
723## License
724See LICENSE in repository for details.
725
726
727## Credits
728**[Leaflet](http://leafletjs.com/)** Is an awesome mapping package.
729
\No newline at end of file