@norconsult-digital-public/isy-map-web-components 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,1401 @@
1
+ # About The Project - IsyMap Web Components
2
+
3
+ IsyMap Web Components is a subset of IsyMap (a openlayers based web map client) build as Web Components (for more information about Web Component visit [webcomponents.org](https://www.webcomponents.org/introduction#how-do-i-use-a-web-component-)). This library enables the use of core IsyMap functionality and can be used with any JavaScript library or framework that works with HTML in modern web browsers.
4
+
5
+ The solution is based on calling a function with given parameters which then returns structured data such as JSON. Basic user interface is available; such as search, map menu and click information.
6
+
7
+ ### Content
8
+ - Getting Startet
9
+ - Prerequisites
10
+ - Enabeling use of UI from IsyMap
11
+ - Usage
12
+ - Navigation
13
+ - Change map content
14
+ - Retriving data
15
+ - Example
16
+
17
+ ## Getting Started
18
+
19
+ ### Prerequisites
20
+ To use isy-map-web-components in your project install it via npm:
21
+ ```sh
22
+ npm i @norconsult-digital/isy-map-web-components
23
+ ```
24
+
25
+ ### React
26
+
27
+ Import modul
28
+ ```js
29
+ import '@norconsult-digital/isy-map-web-components/isy-map-web-components.js'
30
+ ```
31
+ Import style
32
+ ```js
33
+ import '@norconsult-digital/isy-map-web-components/isy-map-web-components.css';
34
+ ```
35
+ The library does not contain the configuration of the IsyMap project. Therfore you have to point the application to the desired configuration.
36
+ ```js
37
+ const [configUrl, setConfigUrl] = useState<string>('https://https://map.isy.no/?project=Plandialog&application=demo&apiKey=XXXXX');
38
+ ```
39
+
40
+ #### Enabeling use of UI from IsyMap
41
+ It's possible to include some UI-components from IsyMap. The availabe components are:
42
+ ```js
43
+ <div className='isygis'>
44
+ <isygis-custom-isy-search-bar /> // Search bar
45
+ <isygis-custom-isy-side-nav /> // Side menu - layers turn on/off
46
+ <isygis-custom-isy-info-panel /> // Info panel - search results / point information
47
+ <isygis-custom-isy-map configurl={configUrl} giparams={JSON.stringify(giParam)} ref={mapOutputRef} /> // Map module
48
+ </div>
49
+ ```
50
+ The className must be set to *isygis* which is used to isolate the bootstrap styles in IsyMap.
51
+
52
+ ### Angular
53
+
54
+ In angular.json include styles and scripts:
55
+
56
+ ```json
57
+ "styles": [
58
+ "src/styles.scss",
59
+ "../node_modules/@norconsult-digital/isy-map-web-components/isy-map-web-components.css"
60
+ ],
61
+
62
+ "scripts": [
63
+ "../node_modules/@norconsult-digital/isy-map-web-components/isy-map-web-components.js"
64
+ ]
65
+ ```
66
+
67
+ In app.modules.ts import:
68
+ ```js
69
+ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
70
+ // Add CUSTOM_ELEMENTS_SCHEMA to @NgModule
71
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
72
+ ```
73
+
74
+ The library does not contain the configuration of the IsyMap project. Therfore you have to point the application to the desired configuration.
75
+ ```js
76
+ configUrl = 'https://map.isy.no/?project=Plandialog&application=demo&apiKey=XXXXX';
77
+ ```
78
+
79
+ #### Enabeling use of UI from IsyMap
80
+ It's possible to include some UI-components from IsyMap. The availabe components are:
81
+ ```js
82
+ <div class="isygis">
83
+ <isygis-custom-isy-search-bar hideMenuIcon='false'></isygis-custom-isy-search-bar> // Search bar with visible menu icon (for open side panel)
84
+ <isygis-custom-isy-side-nav></isygis-custom-isy-side-nav> // Side panel with layers and tools list
85
+ <isygis-custom-isy-info-panel></isygis-custom-isy-info-panel> // Info panel - search results / point information
86
+ <isygis-custom-isy-base-layers></isygis-custom-isy-base-layers> // Base layers switcher
87
+ <isygis-custom-isy-map configurl="{{configUrl}}" [giparams]="giParams" (mapoutput)="mapoutput($event)"></isygis-custom-isy-map> // Map module
88
+ <isygis-custom-isy-tool-draw [giparams]="giParamsDraw"></isygis-custom-isy-tool-draw> // Tool - draw
89
+ </div>
90
+ ```
91
+ The className must be set to *isygis* which is used to isolate the bootstrap styles in IsyMap.
92
+
93
+ ## Table of Contents
94
+ ### Isygis-custom-isy-map
95
+ - [SETTER PARAMETERS *isygis-custom-isy-map*](#setter-parameters-smallisygis-custom-isy-mapsmall)
96
+ - [Switches off the feature selector and drawing manager within the map service.](#switches-off-the-feature-selector-and-drawing-manager-within-the-map-service)
97
+ - [Makes all visible layers on the map invisible.](#makes-all-visible-layers-on-the-map-invisible)
98
+ - [Makes a specific layer invisible on the map, identified by its unique identifier (guid) or by its name.](#makes-a-specific-layer-invisible-on-the-map-identified-by-its-unique-identifier-guid-or-by-its-name)
99
+ - [Makes a specific layer invisible on the map, identified by its name (DEPRECATED)](#makes-a-specific-layer-invisible-on-the-map-identified-by-its-name-deprecated)
100
+ - [Makes a specific layer invisible on the map, identified by its unique identifiers (guids) or by its names.](#makes-a-specific-layer-invisible-on-the-map-identified-by-its-unique-identifiers-guids-or-by-its-names)
101
+ - [Hides a marker displayed on the map.](#hides-a-marker-displayed-on-the-map)
102
+ - [Hides the translation bar.](#hides-the-translation-bar)
103
+ - [Removes a drawing from the GeoJson data. (REMOVED)](#removes-a-drawing-from-the-geojson-data-removed)
104
+ - [Removes a specific layer.](#removes-a-specific-layer)
105
+ - [Removes multiple layers.](#removes-multiple-layers)
106
+ - [Changes the map size.](#changes-the-map-size)
107
+ - [Modifies the map's center.](#modifies-the-maps-center)
108
+ - [Sets the extent of the map.](#sets-the-extent-of-the-map)
109
+ - [Changes the language setting of the map interface.](#changes-the-language-setting-of-the-map-interface)
110
+ - [Provides a new JWT token for the map service.](#provides-a-new-jwt-token-for-the-map-service)
111
+ - [Enables or disables map animations.](#enables-or-disables-map-animations)
112
+ - [Reveals a layer by its unique identifier (guid) or by its name.](#reveals-a-layer-by-its-unique-identifier-guid-or-by-its-name)
113
+ - [Reveals a layer by its name (DEPRECATED)](#reveals-a-layer-by-its-name-deprecated)
114
+ - [Reveals multiple layers identified by their unique identifiers (guids) or by its names.](#reveals-multiple-layers-identified-by-their-unique-identifiers-guids-or-by-its-names)
115
+ - [Enables the functionality to display a marker on a click and hide the marker when it's inactive.](#enables-the-functionality-to-display-a-marker-on-a-click-and-hide-the-marker-when-its-inactive)
116
+ - [Activates the feature to show neighbors.](#activates-the-feature-to-show-neighbors)
117
+ - [Show a property on the map.](#show-a-property-on-the-map)
118
+ - [Show properties on the map](#show-properties-on-the-map)
119
+ - [Uploads GeoJson data for drawing. (REMOVED)](#uploads-geojson-data-for-drawing-removed)
120
+ - [Uploads GeoJson data.](#uploads-geojson-data)
121
+ - [Allows the map to zoom in or out.](#allows-the-map-to-zoom-in-or-out)
122
+ - [Switches off the map hover information.](#switches-off-the-map-hover-information)
123
+ - [Adjusts the click pixel tolerance on the map.](#adjusts-the-click-pixel-tolerance-on-the-map)
124
+ - [Sets the popover parameters.](#sets-the-popover-parameters)
125
+ - [Start / stop geolocation.](#start--stop-geolocation)
126
+ - [Set base layer](#set-base-layer)
127
+ - [Login (AzureAd)](#login-azuread)
128
+ - [Logout (AzureAd)](#logout-azuread)
129
+ - [Show / Hide scale line](#show--hide-scale-line)
130
+ - [Pause hover info](#pause-hover-info)
131
+
132
+ - [GETTERS PARAMETERS <small>isygis-custom-isy-map</small>](#getters-parameters-smallisygis-custom-isy-mapsmall)
133
+ - [Retrieves the center of the map](#retrieves-the-center-of-the-map)
134
+ - [Activates or deactivates the capture of click coordinates on the map](#activates-or-deactivates-the-capture-of-click-coordinates-on-the-map)
135
+ - [Retrieves the extent of the map](#retrieves-the-extent-of-the-map)
136
+ - [Retrieves the overlay layers of the map](#retrieves-the-overlay-layers-of-the-map)
137
+ - [Get base layers](#get-base-layers)
138
+ - [Retrieves the geometry of an object](#retrieves-the-geometry-of-an-object)
139
+ - [Retrieves the project configuration](#retrieves-the-project-configuration)
140
+ - [Retrieves the property information of an object](#retrieves-the-property-information-of-an-object)
141
+ - [Retrieves information about a point](#retrieves-information-about-a-point)
142
+ - [Retrieves Saks information](#retrieves-saks-information)
143
+ - [Retrieves the chosen search result](#retrieves-the-chosen-search-result)
144
+ - [Retrieves the visible layers on the map](#retrieves-the-visible-layers-on-the-map)
145
+ - [Transforms coordinates from one spatial reference to another](#transforms-coordinates-from-one-spatial-reference-to-another)
146
+ - [Retrieves the start of a map move](#retrieves-the-start-of-a-map-move)
147
+ - [Retrieves the end of a map move](#retrieves-the-end-of-a-map-move)
148
+ - [Get neighbors for plan](#get-neighbors-for-plan)
149
+ - [Get map image](#get-map-image)
150
+
151
+ ### Isygis-custom-isy-tool-draw
152
+ - [SETTER PARAMETERS <small>isygis-custom-isy-tool-draw</small>](#setter-parameters-smallisygis-custom-isy-tool-drawsmall)
153
+ - [Show draw tool UI](#show-draw-tool-ui)
154
+ - [Start/Modify/Remove Draw](#startmodifyremove-draw)
155
+ - [IStyle Interface](#istyle-interface)
156
+ - [IFill Interface](#ifill-interface)
157
+ - [IStroke Interface](#istroke-interface)
158
+ - [IImage Interface](#iimage-interface)
159
+ - [Modify Draw](#modify-draw)
160
+ - [Remove Selected Object](#remove-selected-object)
161
+ - [Remove All Drawings](#remove-all-drawings)
162
+ - [Stop draw](#stop-draw)
163
+ - [Upload drawing GeoJson](#upload-drawing-geojson)
164
+
165
+ - [GETTER PARAMETERS <small>isygis-custom-isy-tool-draw</small>](#getter-parameters-smallisygis-custom-isy-tool-drawsmall)
166
+ - [Download drawing GeoJSON](#download-drawing-geojson)
167
+ - [Get modified feature](#get-modified-feature)
168
+ - [Get drawn features](#get-drawn-features)
169
+
170
+
171
+
172
+ # SETTER PARAMETERS <small>isygis-custom-isy-map</small>
173
+ ```js
174
+ <div class="isygis">
175
+ <isygis-custom-isy-map configurl="{{configUrl}}" [giparams]="giparams" (mapoutput)="mapOutputData($event)"></isygis-custom-isy-map>
176
+ </div>
177
+ ```
178
+
179
+ #### Switches off the feature selector and drawing manager within the map service.
180
+ ```javascript
181
+ this.giparams = JSON.stringify({
182
+ giParamId: 'deactivateShowNeighbors',
183
+ });
184
+ ```
185
+
186
+ #### Makes all visible layers on the map invisible.
187
+ ```javascript
188
+ this.giparams = JSON.stringify({
189
+ giParamId: 'hideAllVisibleLayers',
190
+ });
191
+ ```
192
+
193
+ #### Makes a specific layer invisible on the map, identified by its unique identifier (guid) or by its name.
194
+ ```javascript
195
+ this.giparams = JSON.stringify({
196
+ giParamId: 'hideLayer',
197
+ guid?: string,
198
+ name?: string,
199
+ });
200
+ ```
201
+
202
+ #### Makes a specific layer invisible on the map, identified by its name (DEPRECATED)
203
+ ```javascript
204
+ this.giparams = JSON.stringify({
205
+ giParamId: 'hideLayerByName',
206
+ name: string,
207
+ });
208
+ ```
209
+
210
+ #### Makes a specific layer invisible on the map, identified by its unique identifiers (guids) or by its names.
211
+ ```javascript
212
+ this.giparams = JSON.stringify({
213
+ giParamId: 'hideLayers',
214
+ guids?: string[],
215
+ names?: string[],
216
+ });
217
+ ```
218
+
219
+ #### Hides a marker displayed on the map.
220
+ ```javascript
221
+ this.giparams = JSON.stringify({
222
+ giParamId: 'hideMarker',
223
+ });
224
+ ```
225
+
226
+ #### Hides the translation bar.
227
+ ```javascript
228
+ this.giparams = JSON.stringify({
229
+ giParamId: 'hideTranslateBar',
230
+ hide: boolean,
231
+ });
232
+ ```
233
+
234
+ #### Removes a drawing from the GeoJson data. (REMOVED)
235
+ ```javascript
236
+ this.giparams = JSON.stringify({
237
+ giParamId: 'removeDrawingGeoJson',
238
+ });
239
+ ```
240
+
241
+ #### Removes a specific layer.
242
+ ```javascript
243
+ this.giparams = JSON.stringify({
244
+ giParamId: 'removeLayer',
245
+ guid?: string,
246
+ name?: string,
247
+ });
248
+ ```
249
+
250
+ #### Removes multiple layers.
251
+ ```javascript
252
+ this.giparams = JSON.stringify({
253
+ giParamId: 'removeLayers',
254
+ guids?: string[],
255
+ names?: string[],
256
+ });
257
+ ```
258
+
259
+ #### Changes the map size.
260
+ ```javascript
261
+ this.giparams = JSON.stringify({
262
+ giParamId: 'resizeMap',
263
+ });
264
+ ```
265
+
266
+ #### Modifies the map's center.
267
+ ```javascript
268
+ this.giparams = JSON.stringify({
269
+ giParamId: 'setCenter',
270
+ epsg: string,
271
+ lat: number,
272
+ lon: number,
273
+ scale?: number,
274
+ zoom: number,
275
+ showMarker: boolean,
276
+ markerColor?: string,
277
+ });
278
+ ```
279
+
280
+ #### Sets the extent of the map.
281
+ ```javascript
282
+ this.giparams = JSON.stringify({
283
+ giParamId: 'setExtent',
284
+ epsg?: string,
285
+ extent: number[],
286
+ });
287
+ ```
288
+
289
+ #### Changes the language setting of the map interface.
290
+ ```javascript
291
+ this.giparams = JSON.stringify({
292
+ giParamId: 'setLanguage',
293
+ language: string, // ('en', 'no')
294
+ });
295
+ ```
296
+
297
+ #### Provides a new JWT token for the map service.
298
+ ```javascript
299
+ this.giparams = JSON.stringify({
300
+ giParamId: 'setJwt',
301
+ token: string,
302
+ });
303
+ ```
304
+
305
+ #### Enables or disables map animations.
306
+ ```javascript
307
+ this.giparams = JSON.stringify({
308
+ giParamId: 'setMapAnimation',
309
+ active: boolean,
310
+ });
311
+ ```
312
+
313
+ #### Reveals a layer by its unique identifier (guid) or by its name.
314
+ ```javascript
315
+ this.giparams = JSON.stringify({
316
+ giParamId: 'showLayer',
317
+ guid?: string,
318
+ name?: string
319
+ });
320
+ ```
321
+
322
+ #### Reveals a layer by its name (DEPRECATED)
323
+ ```javascript
324
+ this.giparams = JSON.stringify({
325
+ giParamId: 'showLayerByName',
326
+ name: string,
327
+ });
328
+ ```
329
+
330
+ #### Reveals multiple layers identified by their unique identifiers (guids) or by its names.
331
+ ```javascript
332
+ this.giparams = JSON.stringify({
333
+ giParamId: 'showLayers',
334
+ guids?: string[],
335
+ names?: string[],
336
+ });
337
+ ```
338
+
339
+ #### Enables the functionality to display a marker on a click and hide the marker when it's inactive.
340
+ ```javascript
341
+ this.giparams = JSON.stringify({
342
+ giParamId: 'showMarkerWhenClick',
343
+ active: boolean,
344
+ markerColor: string,
345
+ });
346
+ ```
347
+
348
+ #### Activates the feature to show neighbors.
349
+ ```javascript
350
+ this.giparams = JSON.stringify({
351
+ giParamId: 'showNeighbors',
352
+ bruksnummer: string,
353
+ festenummer: string,
354
+ gaardsnummer: string,
355
+ kommunenummer: string,
356
+ seksjonsnummer: string,
357
+ });
358
+ ```
359
+
360
+ #### Show a property on the map.
361
+ ```javascript
362
+ this.giparams = JSON.stringify({
363
+ giParamId: 'showProperty',
364
+ bruksnummer: string,
365
+ festenummer: string,
366
+ gaardsnummer: string,
367
+ kommunenummer: string,
368
+ seksjonsnummer: string,
369
+ showMarker?: boolean, // optional, default is true
370
+ selectFeature?: boolean, // optional for select property
371
+ clearHighlighted?: boolean, // optional, default is true
372
+ jsonStyle?: { // optional, when selectFeature is true and jsonStyle is not defined default color is apply
373
+ stroke: {
374
+ color: "rgba(234,148,158,1)",
375
+ width: 2
376
+ },
377
+ fill: {
378
+ color: "rgba(234,148,158,0.5)"
379
+ }
380
+ },
381
+ layerOrder?: number // optional, default is undefined
382
+ });
383
+ ```
384
+
385
+ #### Show properties on the map.
386
+ ```javascript
387
+ this.giparams = JSON.stringify({
388
+ giParamId: 'showProperties',
389
+ jsonStyle: {
390
+ stroke: {
391
+ color: "rgba(234,148,158,1)",
392
+ width: 2
393
+ },
394
+ fill: {
395
+ color: "rgba(234,148,158,0.5)"
396
+ }
397
+ },
398
+ clearHighlighted?: boolean, // optional, default is true
399
+ properties: [
400
+ {
401
+ bruksnummer: string,
402
+ festenummer: string,
403
+ gaardsnummer: string,
404
+ kommunenummer: string,
405
+ seksjonsnummer: string,
406
+ },
407
+ ...
408
+ ],
409
+ layerOrder?: number // optional, default is undefined
410
+ });
411
+ ```
412
+
413
+ #### Uploads GeoJson data for drawing. (REMOVED)
414
+ Function is moved to: "isygis-custom-isy-tool-draw"
415
+ ```javascript
416
+ const data = {
417
+ type: 'FeatureCollection',
418
+ features: [
419
+ {
420
+ type: 'Feature',
421
+ geometry: {
422
+ type: 'Point',
423
+ coordinates: [10.424242651312149, 63.43525321967451],
424
+ },
425
+ properties: {
426
+ style: {
427
+ fill: { color: 'rgba(0,0,0,0.75)' },
428
+ stroke: { color: 'rgba(0,0,0,1)', width: 2 },
429
+ image: { radius: 7, fill: { color: 'rgba(0,0,0,1)' } },
430
+ text: '',
431
+ textSize: 16,
432
+ },
433
+ },
434
+ id: '3d34a05a-6e26-6943-10e7-ba8a3e48ba34',
435
+ },
436
+ ],
437
+ };
438
+
439
+ this.giparams = JSON.stringify({
440
+ giParamId: 'uploadDrawingGeoJson',
441
+ data: JSON.stringify(data)
442
+ });
443
+ ```
444
+
445
+ #### Uploads GeoJson data.
446
+ ```javascript
447
+ const clusterStyleTest = {
448
+ fill: {
449
+ color: 'rgba(0,0,0,0.75)',
450
+ },
451
+ stroke: {
452
+ color: 'rgba(0,0,0,1)',
453
+ width: 2,
454
+ },
455
+ image: {
456
+ radius: 15,
457
+ fill: {
458
+ color: 'rgba(145,180,73,1)',
459
+ },
460
+ stroke: {
461
+ color: 'rgba(0,0,0,1)',
462
+ width: 2,
463
+ },
464
+ },
465
+ };
466
+
467
+ this.giparams = JSON.stringify({
468
+ giParamId: 'uploadGeoJson',
469
+ layerName: string,
470
+ features: string,
471
+ jsonStyle?: string,
472
+ featureInfoElements?: string,
473
+ featureInfoTitle?: string,
474
+ version?: string,
475
+ typeName?: string,
476
+ cluster?: boolean,
477
+ clustercount?: boolean,
478
+ clusterdistance?: number,
479
+ clusterstyleurl?: string,
480
+ clusterstyle?: JSON.stringify(clusterStyleTest),
481
+ layerOrder?: number,
482
+ });
483
+ ```
484
+ #### Uploads GeoTiff data.
485
+ ```javascript
486
+ this.giparams = JSON.stringify({
487
+ giParamId: 'uploadGeoTiff',
488
+ geoTiffData: 'data:image/tiff;base64,SUkqAAgAAAARA...',
489
+ fitExtent?: true,
490
+ layerName?: string,
491
+ layerOrder?: number,
492
+ removePreviousGeoTiff?: boolean
493
+ });
494
+ ```
495
+ #### Switches off the map baselayers
496
+ ```javascript
497
+ this.giparams = JSON.stringify({
498
+ giParamId: 'hideBaseLayers',
499
+ });
500
+ ```
501
+ #### Switches on the map baselayers
502
+ ```javascript
503
+ this.giparams = JSON.stringify({
504
+ giParamId: 'showBaseLayers',
505
+ });
506
+ ```
507
+ #### Cache required configuration
508
+ ```javascript
509
+ this.giparams = JSON.stringify({
510
+ giParamId: 'cacheConfig',
511
+ offline: boolean,
512
+ });
513
+ ```
514
+ #### Allows the map to zoom in or out.
515
+ ```javascript
516
+ this.giparams = JSON.stringify({
517
+ giParamId: 'zoomIn',
518
+ });
519
+
520
+ this.giparams = JSON.stringify({
521
+ giParamId: 'zoomOut',
522
+ });
523
+ ```
524
+
525
+ #### Switches off the map hover information.
526
+ ```javascript
527
+ this.giparams = JSON.stringify({
528
+ giParamId: 'deactivateHoverInfo',
529
+ hide: boolean,
530
+ });
531
+ ```
532
+
533
+ #### Adjusts the click pixel tolerance on the map.
534
+ ```javascript
535
+ this.giparams = JSON.stringify({
536
+ giParamId: 'setClickPixelTolerance',
537
+ clickPixelTolerance: number,
538
+ });
539
+ ```
540
+
541
+ #### Sets the popover parameters.
542
+ ```javascript
543
+ this.giparams = JSON.stringify({
544
+ giParamId: 'setPopoverParams',
545
+ layerName: string,
546
+ template: string,
547
+ popoverColor: string,
548
+ });
549
+ ```
550
+
551
+ #### Start / stop geolocation.
552
+ ```javascript
553
+ this.giparams = JSON.stringify({
554
+ giParamId: 'geolocation',
555
+ active: boolean,
556
+ accuracyCircleFillColor?: string,
557
+ geolocationImageSrc?: string,
558
+ geolocationImageScale?: number,
559
+ });
560
+ ```
561
+
562
+ #### Set base layer
563
+ ```javascript
564
+ this.giparams = JSON.stringify({
565
+ giParamId: 'setBaseLayer',
566
+ guid: string,
567
+ });
568
+ ```
569
+
570
+ #### Login (AzureAd)
571
+ ```javascript
572
+ this.giparams = JSON.stringify({
573
+ giParamId: 'login'
574
+ });
575
+ ```
576
+
577
+ #### Logout (AzureAd)
578
+ ```javascript
579
+ this.giparams = JSON.stringify({
580
+ giParamId: 'logout'
581
+ });
582
+ ```
583
+
584
+ #### Show / Hide scale line
585
+ Default is false (scale line is hidden)
586
+ ```javascript
587
+ this.giparams = JSON.stringify({
588
+ giParamId: 'showScaleLine',
589
+ show: boolean
590
+ });
591
+ ```
592
+
593
+ #### Show / Hide north arrow
594
+ Default is false (north arrow is hidden)
595
+ ```javascript
596
+ this.giparams = JSON.stringify({
597
+ giParamId: 'showNorthArrow',
598
+ show: boolean // default: false
599
+ northArrowUrl: string // './assets/img/northArrow1.png'
600
+ });
601
+ ```
602
+
603
+ #### Pause hover info
604
+ ```javascript
605
+ this.giparams = JSON.stringify({
606
+ giParamId: 'pauseHoverInfo',
607
+ active: boolean,
608
+ });
609
+ ```
610
+
611
+ # GETTERS PARAMETERS <small>isygis-custom-isy-map</small>
612
+ ```js
613
+ <div class="isygis">
614
+ <isygis-custom-isy-map configurl="{{configUrl}}" [giparams]="giparams" (mapoutput)="mapOutputData($event)"></isygis-custom-isy-map>
615
+ </div>
616
+ ```
617
+
618
+ #### Retrieves the center of the map
619
+ ```javascript
620
+ this.giparams = JSON.stringify({
621
+ giParamId: 'getCenter',
622
+ });
623
+ ```
624
+
625
+ _Returned event data_
626
+ ```javascript
627
+ data = {
628
+ "mapCenter": {
629
+ "epsg": "EPSG:25832",
630
+ "lon": 584472,
631
+ "lat": 7001764,
632
+ "zoom": 5,
633
+ "scale": 2560000
634
+ },
635
+ "mapOutputId": "mapCenter"
636
+ }
637
+
638
+ mapOutputData(data: any): void {
639
+ if (data['mapOutputId'] === 'mapCenter') {
640
+ // ...
641
+ }
642
+ }
643
+ ```
644
+
645
+ #### Activates or deactivates the capture of click coordinates on the map
646
+ ```javascript
647
+ this.giparams = JSON.stringify({
648
+ giParamId: 'getClickCoordinates',
649
+ active: boolean,
650
+ });
651
+ ```
652
+
653
+ _Returned event data_
654
+ ```javascript
655
+ data = {
656
+ "mapClickedCoordinates": {
657
+ "coordinate": [
658
+ 587748.5742187499,
659
+ 6942447.1640625
660
+ ],
661
+ "pixel": [
662
+ 782.33984375,
663
+ 487.6171875
664
+ ],
665
+ "client": [
666
+ 890.30859375,
667
+ 592.6171875
668
+ ],
669
+ "epsg": "EPSG:25832",
670
+ "zoom": 5
671
+ },
672
+ "mapOutputId": "mapClickedCoordinates"
673
+ }
674
+
675
+ mapOutputData(data: any): void {
676
+ if (data['mapOutputId'] === 'mapClickedCoordinates') {
677
+ // ...
678
+ }
679
+ }
680
+ ```
681
+
682
+ #### Retrieves the extent of the map
683
+ ```javascript
684
+ this.giparams = JSON.stringify({
685
+ giParamId: 'getExtent',
686
+ epsg?: string,
687
+ });
688
+ ```
689
+
690
+ _Returned event data_
691
+ ```javascript
692
+ data = {
693
+ "mapExtent": {
694
+ "epsg": "EPSG:25832",
695
+ "extent": [
696
+ 58104.5,
697
+ 6730964,
698
+ 1110839.5,
699
+ 7272564
700
+ ]
701
+ },
702
+ "mapOutputId": "mapExtent"
703
+ }
704
+
705
+ mapOutputData(data: any): void {
706
+ if (data['mapOutputId'] === 'mapExtent') {
707
+ // ...
708
+ }
709
+ }
710
+ ```
711
+
712
+ #### Retrieves the overlay layers of the map
713
+ ```javascript
714
+ this.giparams = JSON.stringify({
715
+ giParamId: 'getMapLayers',
716
+ });
717
+ ```
718
+
719
+ _Returned event data_
720
+ ```javascript
721
+ data = {
722
+ "mapLayers": [
723
+ { ... },
724
+ ],
725
+ "mapOutputId": "mapLayers"
726
+ }
727
+
728
+ mapOutputData(data: any): void {
729
+ if (data['mapOutputId'] === 'mapLayers') {
730
+ // ...
731
+ }
732
+ }
733
+ ```
734
+
735
+ #### Get base layers
736
+ ```javascript
737
+ this.giparams = JSON.stringify({
738
+ giParamId: 'getBaseLayers',
739
+ });
740
+ ```
741
+
742
+ _Returned event data_
743
+ ```javascript
744
+ data = {
745
+ "baseLayers": [
746
+ ...
747
+ ],
748
+ "mapOutputId": "baseLayers"
749
+ }
750
+
751
+ mapOutputData(data: any): void {
752
+ if (data['mapOutputId'] === 'baseLayers') {
753
+ // ...
754
+ }
755
+ }
756
+ ```
757
+
758
+ #### Retrieves the geometry of an object
759
+ ```javascript
760
+ this.giparams = JSON.stringify({
761
+ giParamId: 'getObjectGeometry',
762
+ active: boolean,
763
+ notAllowSameCoordsZoom?: boolean, // default is false
764
+ threshold?: number, // default is 1 m
765
+ disableClusterZoom?: boolean // default is false
766
+ });
767
+ ```
768
+
769
+ _Returned event data_
770
+ ```javascript
771
+ data = {
772
+ "objectGeometry": { ... },
773
+ "mapOutputId": "objectGeometry"
774
+ }
775
+
776
+ mapOutputData(data: any): void {
777
+ if (data['mapOutputId'] === 'objectGeometry') {
778
+ // ...
779
+ }
780
+ }
781
+ ```
782
+
783
+ #### Retrieves the project configuration
784
+ ```javascript
785
+ this.giparams = JSON.stringify({
786
+ giParamId: 'getProjectConfig',
787
+ active: boolean,
788
+ });
789
+ ```
790
+
791
+ _Returned event data_
792
+ ```javascript
793
+ data = {
794
+ "projectConfig": { ... },
795
+ "mapOutputId": "projectConfig"
796
+ }
797
+
798
+ mapOutputData(data: any): void {
799
+
800
+
801
+ if (data['mapOutputId'] === 'projectConfig') {
802
+ // ...
803
+ }
804
+ }
805
+ ```
806
+
807
+ #### Retrieves the property information of an object
808
+ ```javascript
809
+ this.giparams = JSON.stringify({
810
+ giParamId: 'getPropertyInfo',
811
+ active: boolean,
812
+ });
813
+ ```
814
+
815
+ _Returned event data_
816
+ ```javascript
817
+ data = {
818
+ "propertyInfo": { ... },
819
+ "mapOutputId": "propertyInfo"
820
+ }
821
+
822
+ mapOutputData(data: any): void {
823
+ if (data['mapOutputId'] === 'propertyInfo') {
824
+ // ...
825
+ }
826
+ }
827
+ ```
828
+
829
+ #### Retrieves information about a point
830
+ ```javascript
831
+ this.giparams = JSON.stringify({
832
+ giParamId: 'getPointInfo',
833
+ active: boolean,
834
+ });
835
+ ```
836
+
837
+ _Returned event data_
838
+ ```javascript
839
+ data = {
840
+ "pointInfo": { ... },
841
+ "mapOutputId": "pointInfo"
842
+ }
843
+
844
+ mapOutputData(data: any): void {
845
+ if (data['mapOutputId'] === 'pointInfo') {
846
+ // ...
847
+ }
848
+ }
849
+ ```
850
+
851
+ #### Retrieves Saks information
852
+ ```javascript
853
+ this.giparams = JSON.stringify({
854
+ giParamId: 'getSaksInfo',
855
+ knr: string,
856
+ planid: string,
857
+ });
858
+ ```
859
+
860
+ _Returned event data_
861
+ ```javascript
862
+ data = {
863
+ "saksInfo": { ... },
864
+ "mapOutputId": "saksInfo"
865
+ }
866
+
867
+ mapOutputData(data: any): void {
868
+ if (data['mapOutputId'] === 'saksInfo') {
869
+ // ...
870
+ }
871
+ }
872
+ ```
873
+
874
+ #### Retrieves the chosen search result
875
+ ```javascript
876
+ this.giparams = JSON.stringify({
877
+ giParamId: 'getChosenSearchResult',
878
+ active: boolean,
879
+ });
880
+ ```
881
+
882
+ _Returned event data_
883
+ ```javascript
884
+ data = {
885
+ "chosenSearchResult": { ... },
886
+ "mapOutputId": "chosenSearchResult"
887
+ }
888
+
889
+ mapOutputData(data: any): void {
890
+ if (data['mapOutputId'] === 'chosenSearchResult') {
891
+ // ...
892
+ }
893
+ }
894
+ ```
895
+
896
+ #### Retrieves the visible layers on the map
897
+ ```javascript
898
+ this.giparams = JSON.stringify({
899
+ giParamId: 'getVisibleLayers',
900
+ active: boolean,
901
+ });
902
+ ```
903
+
904
+ _Returned event data_
905
+ ```javascript
906
+ data = {
907
+ "visibleLayers": [
908
+ { ... },
909
+ ],
910
+ "mapOutputId": "visibleLayers"
911
+ }
912
+
913
+ mapOutputData(data: any): void {
914
+ if (data['mapOutputId'] === 'visibleLayers') {
915
+ // ...
916
+ }
917
+ }
918
+ ```
919
+
920
+ #### Transforms coordinates from one spatial reference to another
921
+ ```javascript
922
+ this.giparams = JSON.stringify({
923
+ giParamId: 'getTransformCoordinates',
924
+ fromEpsg: string,
925
+ toEpsg: string,
926
+ coordinates: number[],
927
+ });
928
+ ```
929
+
930
+ _Returned event data_
931
+ ```javascript
932
+ data = {
933
+ "coordinates": [
934
+ 10.109541122770539,
935
+ 59.80633928908478
936
+ ],
937
+ "mapOutputId": "transformCoordinates"
938
+ }
939
+
940
+ mapOutputData(data: any): void {
941
+ if (data['mapOutputId'] === 'transformCoordinates') {
942
+ // ...
943
+ }
944
+ }
945
+ ```
946
+
947
+ #### Retrieves the start of a map move
948
+ ```javascript
949
+ this.giparams = JSON.stringify({
950
+ giParamId: 'getMapMoveStart',
951
+ active: boolean,
952
+ });
953
+ ```
954
+
955
+ _Returned event data_
956
+ ```javascript
957
+ data = {
958
+ "mapOutputId": "mapMoveStart"
959
+ }
960
+
961
+ mapOutputData(data: any): void {
962
+ if (data['mapOutputId'] === 'mapMoveStart') {
963
+ //
964
+
965
+ ...
966
+ }
967
+ }
968
+ ```
969
+
970
+ #### Retrieves the end of a map move
971
+ ```javascript
972
+ this.giparams = JSON.stringify({
973
+ giParamId: 'getMapMoveEnd',
974
+ active: boolean,
975
+ });
976
+ ```
977
+
978
+ _Returned event data_
979
+ ```javascript
980
+ data = {
981
+ "mapOutputId": "mapMoveEnd",
982
+ "extent": [
983
+ 10.38607418832841,
984
+ 63.41947665736146,
985
+ 10.477919652427957,
986
+ 63.43754761711075
987
+ ],
988
+ "center": {
989
+ "epsg": "EPSG:32632",
990
+ "lon": 571463.8558368683,
991
+ "lat": 7034133.836538312,
992
+ "zoom": 13,
993
+ "scale": 10000
994
+ }
995
+ }
996
+
997
+ mapOutputData(data: any): void {
998
+ if (data['mapOutputId'] === 'mapMoveEnd') {
999
+ // ...
1000
+ }
1001
+ }
1002
+ ```
1003
+
1004
+ #### Get neighbors for plan
1005
+ ```javascript
1006
+ this.giparams = JSON.stringify({
1007
+ giParamId: 'getNeighborsForPlan',
1008
+ planId: string,
1009
+ kommunenummer: number,
1010
+ });
1011
+ ```
1012
+
1013
+ _Returned event data_
1014
+ ```javascript
1015
+ data = {
1016
+ "mapOutputId": "neighborListForPlan",
1017
+ "Feil": null,
1018
+ "Matrikkelenheter": [
1019
+ {
1020
+ "Id": number,
1021
+ "Knr": number,
1022
+ "Gnr": number,
1023
+ "Bnr": number,
1024
+ "Fnr": number,
1025
+ "Snr": number,
1026
+ "Bruksnavn": string,
1027
+ "Owners": null,
1028
+ "Addresser": [
1029
+ {
1030
+ "Id": number,
1031
+ "Name": string,
1032
+ "ZipCode": number,
1033
+ "PostalCode": string
1034
+ }
1035
+ ],
1036
+ "Teiger": [
1037
+ {
1038
+ "Id": number,
1039
+ "Hovedteig": boolean
1040
+ }
1041
+ ],
1042
+ "Utgatt": boolean
1043
+ }
1044
+ ]
1045
+ }
1046
+
1047
+ mapOutputData(data: any): void {
1048
+ if (data['mapOutputId'] === 'neighborListForPlan') {
1049
+ // ...
1050
+ }
1051
+ }
1052
+ ```
1053
+
1054
+ #### Get map image
1055
+ ```javascript
1056
+ this.giparams = JSON.stringify({
1057
+ giParamId: 'getMapImage',
1058
+ format: string; // 'image/png' | 'image/jpeg'
1059
+ name?: string; // default: 'map'
1060
+ download?: boolean; // default: false
1061
+ showNorthArrow?: boolean; // default: false
1062
+ northArrowUrl?: string; // './assets/img/northArrow1.png'
1063
+ showScaleLine?: boolean; // default: false
1064
+ });
1065
+ ```
1066
+
1067
+ _Returned event data_
1068
+ ```javascript
1069
+ // When download is true, only the image is downloaded. When download is false, mapOutput is returned.
1070
+ data = {
1071
+ "mapOutputId": "mapImage",
1072
+ "mapImage": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABLAA....",
1073
+ }
1074
+
1075
+ mapOutputData(data: any): void {
1076
+ if (data['mapOutputId'] === 'mapImage') {
1077
+ // ...
1078
+ }
1079
+ }
1080
+ ```
1081
+
1082
+ # SETTER PARAMETERS <small>isygis-custom-isy-tool-draw</small>
1083
+ ```js
1084
+ <div class="isygis">
1085
+ <isygis-custom-isy-tool-draw [giparams]="giparamsDraw"></isygis-custom-isy-map>
1086
+ </div>
1087
+ ```
1088
+ #### Show draw tool UI
1089
+ ```javascript
1090
+ this.giparamsDraw = JSON.stringify({
1091
+ giParamId: 'showDrawToolUi';
1092
+ show: boolean;
1093
+ minified?: boolean;
1094
+ drawPanels?: string[]; // 'pointTab' | 'lineTab' | 'polygonTab'
1095
+ });
1096
+ ```
1097
+
1098
+ #### Start/Modify/Remove Draw
1099
+ ```javascript
1100
+ this.giparamsDraw = JSON.stringify({
1101
+ giParamId: 'startDraw',
1102
+ type?: string, // 'Point' | 'LineString' | 'Polygon';
1103
+ style?: IStyle,
1104
+ snapGuides?: boolean
1105
+ });
1106
+
1107
+ interface IStyle {
1108
+ fill: IFill;
1109
+ stroke: IStroke;
1110
+ image: IIMage;
1111
+ }
1112
+
1113
+ interface IFill {
1114
+ color: string;
1115
+ }
1116
+
1117
+ interface IStroke {
1118
+ color: string;
1119
+ lineDash: number[];
1120
+ width: number;
1121
+ }
1122
+
1123
+ interface IIMage {
1124
+ fill: IFill;
1125
+ radius: number;
1126
+ }
1127
+
1128
+ this.giparamsDraw = JSON.stringify({
1129
+ giParamId: 'modifyDraw'
1130
+ });
1131
+
1132
+ this.giparamsDraw = JSON.stringify({
1133
+ giParamId: 'removeSelectedObject'
1134
+ });
1135
+
1136
+ this.giparamsDraw = JSON.stringify({
1137
+ giParamId: 'removeAllDrawings'
1138
+ });
1139
+
1140
+ ```
1141
+
1142
+ #### Stop draw
1143
+ ```javascript
1144
+ this.giparamsDraw = JSON.stringify({
1145
+ giParamId: 'stopDraw';
1146
+ });
1147
+ ```
1148
+
1149
+ #### Upload drawing GeoJson
1150
+ ```javascript
1151
+ const data = {
1152
+ type: 'FeatureCollection',
1153
+ features: [
1154
+ {
1155
+ type: 'Feature',
1156
+ geometry: {
1157
+ type: 'Point',
1158
+ coordinates: [10.424242651312149, 63.43525321967451],
1159
+ },
1160
+ properties: {
1161
+ style: {
1162
+ fill: { color: 'rgba(0,0,0,0.75)' },
1163
+ stroke: { color: 'rgba(0,0,0,1)', width: 2 },
1164
+ image: { radius: 7, fill: { color: 'rgba(0,0,0,1)' } },
1165
+ text: '',
1166
+ textSize: 16,
1167
+ },
1168
+ },
1169
+ id: '3d34a05a-6e26-6943-10e7-ba8a3e48ba34',
1170
+ },
1171
+ ],
1172
+ };
1173
+
1174
+ this.giparamsDraw = JSON.stringify({
1175
+ giParamId: 'uploadDrawingGeoJson',
1176
+ data: JSON.stringify(data)
1177
+ });
1178
+ ```
1179
+ # GETTER PARAMETERS <small>isygis-custom-isy-tool-draw</small>
1180
+
1181
+ ```js
1182
+ <div class="isygis">
1183
+ <isygis-custom-isy-tool-draw [giparams]="giparamsDraw"></isygis-custom-isy-map>
1184
+ </div>
1185
+ ```
1186
+
1187
+ #### Download drawing GeoJSON
1188
+ ```javascript
1189
+ this.giparamsDraw = JSON.stringify({
1190
+ giParamId: 'downloadDrawingGeoJson'
1191
+ });
1192
+ ```
1193
+
1194
+ _Returned_
1195
+ ```javascript
1196
+ GeoJson file.
1197
+ ```
1198
+
1199
+ #### Get modified feature
1200
+ ```javascript
1201
+ this.giparamsDraw = JSON.stringify({
1202
+ giParamId: 'getModifiedFeature',
1203
+ active: boolean
1204
+ });
1205
+ ```
1206
+
1207
+ _Returned_
1208
+ ```javascript
1209
+ data = {
1210
+ "modifiedFeature": {
1211
+ "type": "FeatureCollection",
1212
+ "features": [
1213
+ {
1214
+ "type": "Feature",
1215
+ "geometry": {
1216
+ "type": "LineString",
1217
+ "coordinates": [
1218
+ [
1219
+ 10.412232651157716,
1220
+ 63.43262811826044
1221
+ ],
1222
+ [
1223
+ 10.411910653890358,
1224
+ 63.43217732605416
1225
+ ],
1226
+ [
1227
+ 10.410883819717013,
1228
+ 63.43212704822067
1229
+ ],
1230
+ [
1231
+ 10.410098602316635,
1232
+ 63.43252723949083
1233
+ ]
1234
+ ]
1235
+ },
1236
+ "properties": {
1237
+ "measurement": "163.2 m",
1238
+ "style": {
1239
+ "fill": {
1240
+ "color": "rgba(0, 0, 160, 0.2)"
1241
+ },
1242
+ "stroke": {
1243
+ "color": "black",
1244
+ "lineDash": [
1245
+ 0,
1246
+ 0
1247
+ ],
1248
+ "width": 2
1249
+ },
1250
+ "image": {
1251
+ "fill": {
1252
+ "color": "rgba(0, 0, 0, 1)"
1253
+ },
1254
+ "radius": 5
1255
+ }
1256
+ }
1257
+ },
1258
+ "id": "ff08fa45-3a0d-cf36-3314-d2f396fa89f4"
1259
+ }
1260
+ ]
1261
+ },
1262
+ "mapOutputId": "modifiedFeature"
1263
+ }
1264
+
1265
+ mapOutputData(data: any): void {
1266
+ if (data['mapOutputId'] === 'modifiedFeature') {
1267
+ // ...
1268
+ }
1269
+ }
1270
+ ```
1271
+
1272
+ #### Get drawn features
1273
+ ```javascript
1274
+ this.giparamsDraw = JSON.stringify({
1275
+ giParamId: 'getDrawnFeatures'
1276
+ });
1277
+ ```
1278
+
1279
+ _Returned_
1280
+ ```javascript
1281
+ data = {
1282
+ "drawnFeatures": {
1283
+ "type": "FeatureCollection",
1284
+ "features": [
1285
+ {
1286
+ "type": "Feature",
1287
+ "geometry": {
1288
+ "type": "Point",
1289
+ "coordinates": [
1290
+ 10.412123247109973,
1291
+ 63.432692693888605
1292
+ ]
1293
+ },
1294
+ "properties": {
1295
+ "style": {
1296
+ "fill": {
1297
+ "color": "rgba(0, 0, 160, 0.2)"
1298
+ },
1299
+ "stroke": {
1300
+ "color": "black",
1301
+ "lineDash": [
1302
+ 0,
1303
+ 0
1304
+ ],
1305
+ "width": 2
1306
+ },
1307
+ "image": {
1308
+ "fill": {
1309
+ "color": "rgba(0, 0, 0, 1)"
1310
+ },
1311
+ "radius": 5
1312
+ }
1313
+ }
1314
+ },
1315
+ "id": "80d4f955-9c65-551a-c5bc-748818b5ee11"
1316
+ },
1317
+ {
1318
+ "type": "Feature",
1319
+ "geometry": {
1320
+ "type": "Point",
1321
+ "coordinates": [
1322
+ 10.412654541861299,
1323
+ 63.43263287645137
1324
+ ]
1325
+ },
1326
+ "properties": {
1327
+ "style": {
1328
+ "fill": {
1329
+ "color": "rgba(0, 0, 160, 0.2)"
1330
+ },
1331
+ "stroke": {
1332
+ "color": "black",
1333
+ "lineDash": [
1334
+ 0,
1335
+ 0
1336
+ ],
1337
+ "width": 2
1338
+ },
1339
+ "image": {
1340
+ "fill": {
1341
+ "color": "rgba(0, 0, 0, 1)"
1342
+ },
1343
+ "radius": 5
1344
+ }
1345
+ }
1346
+ },
1347
+ "id": "68807574-6e77-87b9-7bee-958196d27673"
1348
+ }
1349
+ ]
1350
+ },
1351
+ "mapOutputId": "drawnFeatures"
1352
+ }
1353
+
1354
+ mapOutputData(data: any): void {
1355
+ if (data['mapOutputId'] === 'drawnFeatures') {
1356
+ // ...
1357
+ }
1358
+ }
1359
+ ```
1360
+
1361
+ ## Example
1362
+
1363
+ This is an example of using IsyMap Web Components with React
1364
+ ```js
1365
+ const [configUrl, setConfigUrl] = useState<string>('https://map.isy.no/?project=Plandialog&application=demo&apiKey=XXXXX'); //
1366
+ const [giParam, setGiParam] = useState<IParamId>({});
1367
+ const [mapOutput, setMapOutput] = useState<any>({});
1368
+ const mapOutputRef = useRef<any>(null);
1369
+
1370
+ useEffect(() => {
1371
+ if (Object.keys(giParam).length !== 0) {
1372
+ const effect = async () => {
1373
+ setGiParam({});
1374
+ };
1375
+ effect();
1376
+ }
1377
+ }, [giParam]);
1378
+
1379
+ useEffect(() => {
1380
+ const handleEvent = (e: any) => {
1381
+ setMapOutput(e.detail)
1382
+ };
1383
+ mapOutputRef.current.addEventListener('mapoutput', handleEvent);
1384
+ return () => {
1385
+ mapOutputRef.current.removeEventListener('mapoutput', handleEvent);
1386
+ };
1387
+ }, [mapOutput]);
1388
+
1389
+ <div className='isygis'>
1390
+ <isygis-custom-isy-search-bar hideMenuIcon='false' /> // Search bar with visible menu icon (for open side panel)
1391
+ <isygis-custom-isy-side-nav /> // Side menu - layers turn on/off
1392
+ <isygis-custom-isy-info-panel /> // Info panel - search results / point information
1393
+ <isygis-custom-isy-base-layers /> // Base layers switcher
1394
+ <isygis-custom-isy-map configurl={configUrl} giparams={JSON.stringify(giParam)} ref={mapOutputRef} /> // Map module
1395
+ </div>
1396
+ configUrl - input for start project / change project (https://map.isy.no/?project=Plandialog&application=demo&apiKey=XXXXX)
1397
+ giparams - input params - (object with params)
1398
+ className='isygis' - isolation bootstrap styles
1399
+ mapOutputRef = ref for map events
1400
+
1401
+ ```