Skip to content

Commit cd0e34e

Browse files
Rémi Haujeremy-eychenne
authored andcommitted
feat(interaction): snap interaction
Closes #335
1 parent 240f2d4 commit cd0e34e

File tree

5 files changed

+192
-0
lines changed

5 files changed

+192
-0
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
2+
import { Collection, Feature } from 'ol';
3+
import { ObjectEvent } from 'ol/Object';
4+
import { Snap } from 'ol/interaction';
5+
import { MapComponent } from '../map.component';
6+
import { SnapEvent } from 'ol/events/SnapEvent';
7+
import { Segmenters } from 'ol/interaction/Snap';
8+
import VectorSource from 'ol/source/Vector';
9+
import { Geometry } from 'ol/geom';
10+
11+
@Component({
12+
selector: 'aol-interaction-snap',
13+
template: '',
14+
standalone: true,
15+
})
16+
export class SnapInteractionComponent implements OnInit, OnDestroy {
17+
@Input()
18+
features?: Collection<Feature>;
19+
@Input()
20+
source?: VectorSource<Feature<Geometry>>;
21+
@Input()
22+
edge?: boolean;
23+
@Input()
24+
vertex?: boolean;
25+
@Input()
26+
intersection?: boolean;
27+
@Input()
28+
pixelTolerance?: number;
29+
@Input()
30+
segmenters?: Segmenters;
31+
32+
@Output()
33+
olChange: EventEmitter<SnapEvent>;
34+
@Output()
35+
propertyChange: EventEmitter<ObjectEvent>;
36+
@Output()
37+
snap: EventEmitter<SnapEvent>;
38+
@Output()
39+
unsnap: EventEmitter<SnapEvent>;
40+
41+
instance: Snap;
42+
43+
constructor(private map: MapComponent) {
44+
this.olChange = new EventEmitter<SnapEvent>();
45+
this.propertyChange = new EventEmitter<ObjectEvent>();
46+
this.snap = new EventEmitter<SnapEvent>();
47+
this.unsnap = new EventEmitter<SnapEvent>();
48+
}
49+
50+
ngOnInit(): void {
51+
this.instance = new Snap(this);
52+
53+
this.instance.on('change', (event: SnapEvent) => this.olChange.emit(event));
54+
this.instance.on('propertychange', (event: ObjectEvent) => this.propertyChange.emit(event));
55+
this.instance.on('snap', (event: SnapEvent) => this.snap.emit(event));
56+
this.instance.on('unsnap', (event: SnapEvent) => this.unsnap.emit(event));
57+
58+
this.map.instance.addInteraction(this.instance);
59+
}
60+
61+
ngOnDestroy(): void {
62+
this.map.instance.removeInteraction(this.instance);
63+
}
64+
}

projects/ngx-openlayers/src/public-api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import { AttributionsComponent } from './lib/attributions.component';
7676
import { AttributionComponent } from './lib/attribution.component';
7777
import { SourceUTFGridComponent } from './lib/sources/utfgrid.component';
7878
import { LayerComponent } from './lib/layers/layer.component';
79+
import { SnapInteractionComponent } from './lib/interactions/snap.component';
7980

8081
export {
8182
MapComponent,
@@ -148,6 +149,7 @@ export {
148149
SelectInteractionComponent,
149150
ModifyInteractionComponent,
150151
TranslateInteractionComponent,
152+
SnapInteractionComponent,
151153
OverlayComponent,
152154
ContentComponent,
153155
AttributionsComponent,

src/app/app.routing.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { TileJsonComponent } from './tile-json/tile-json.component';
2424
import { SelectInteractionComponent } from './select-interaction/select-interaction.component';
2525
import { ImageStaticComponent } from './image-static/image-static.component';
2626
import { GraticuleComponent } from './graticule/graticule.component';
27+
import { SnapInteractionComponent } from './snap-interaction/snap-interaction.component';
2728

2829
export const routes: Routes = [
2930
{ path: '', component: ExamplesListComponent },
@@ -54,6 +55,7 @@ export const routes: Routes = [
5455
{ path: 'select-interaction', component: SelectInteractionComponent },
5556
{ path: 'tile-json', component: TileJsonComponent },
5657
{ path: 'graticule', component: GraticuleComponent },
58+
{ path: 'snap', component: SnapInteractionComponent },
5759
],
5860
},
5961
{ path: '**', redirectTo: '' },

src/app/example-list.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,10 @@ export const examplesList = [
140140
routerLink: 'graticule',
141141
openLayersLink: 'https://openlayers.org/en/latest/examples/graticule.html',
142142
},
143+
{
144+
title: 'Snap',
145+
description: 'Example of snap interaction',
146+
routerLink: 'snap',
147+
openLayersLink: 'https://openlayers.org/en/latest/examples/snap.html',
148+
},
143149
];
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { Component, signal } from '@angular/core';
2+
import { Feature as GeoJSonFeature, Polygon as GeoJSonPolygon } from 'geojson';
3+
import {
4+
CollectionCoordinatesComponent,
5+
CoordinateComponent,
6+
DefaultInteractionComponent,
7+
FeatureComponent,
8+
GeometryPolygonComponent,
9+
LayerTileComponent,
10+
LayerVectorComponent,
11+
MapComponent,
12+
SnapInteractionComponent as NgxOlSnapInteractionComponent,
13+
SourceOsmComponent,
14+
SourceVectorComponent,
15+
ViewComponent,
16+
} from 'ngx-openlayers';
17+
import { SnapEvent } from 'ol/events/SnapEvent';
18+
19+
@Component({
20+
selector: 'app-snap-interaction',
21+
template: `
22+
<aol-map #map width="100%" height="100%">
23+
<aol-interaction-default></aol-interaction-default>
24+
25+
<aol-view [zoom]="5">
26+
<aol-coordinate [x]="1.4886" [y]="43.5554" [srid]="'EPSG:4326'"></aol-coordinate>
27+
</aol-view>
28+
29+
<aol-layer-tile [opacity]="1">
30+
<aol-source-osm></aol-source-osm>
31+
</aol-layer-tile>
32+
33+
<aol-layer-vector>
34+
<aol-source-vector #vectorSource>
35+
<aol-feature>
36+
<aol-geometry-polygon>
37+
<aol-collection-coordinates [coordinates]="feature.geometry.coordinates" [srid]="'EPSG:4326'">
38+
</aol-collection-coordinates>
39+
</aol-geometry-polygon>
40+
</aol-feature>
41+
</aol-source-vector>
42+
</aol-layer-vector>
43+
<aol-interaction-snap
44+
[pixelTolerance]="50"
45+
[source]="vectorSource.instance"
46+
(snap)="onSnap($event)"
47+
(unsnap)="onUnsnap($event)"
48+
>
49+
</aol-interaction-snap>
50+
</aol-map>
51+
52+
<div class="info">
53+
<h3>Result</h3>
54+
<p>Snapped : {{ snapped() }}</p>
55+
</div>
56+
`,
57+
styles: [
58+
`
59+
:host {
60+
height: 100%;
61+
display: flex;
62+
}
63+
64+
aol-map {
65+
width: 70%;
66+
}
67+
68+
.info {
69+
width: 28%;
70+
padding: 1rem;
71+
}
72+
`,
73+
],
74+
imports: [
75+
MapComponent,
76+
DefaultInteractionComponent,
77+
ViewComponent,
78+
CoordinateComponent,
79+
LayerTileComponent,
80+
SourceOsmComponent,
81+
LayerVectorComponent,
82+
SourceVectorComponent,
83+
FeatureComponent,
84+
GeometryPolygonComponent,
85+
CollectionCoordinatesComponent,
86+
NgxOlSnapInteractionComponent,
87+
],
88+
})
89+
export class SnapInteractionComponent {
90+
feature: GeoJSonFeature<GeoJSonPolygon> = {
91+
geometry: {
92+
coordinates: [
93+
[
94+
[-1.7138671875, 43.35713822211053],
95+
[4.515380859375, 43.35713822211053],
96+
[4.515380859375, 47.76886840424207],
97+
[-1.7138671875, 47.76886840424207],
98+
[-1.7138671875, 43.35713822211053],
99+
],
100+
],
101+
type: 'Polygon',
102+
},
103+
properties: {},
104+
type: 'Feature',
105+
};
106+
107+
snapped = signal(false);
108+
109+
onSnap(event: SnapEvent): void {
110+
console.log(event);
111+
this.snapped.set(true);
112+
}
113+
114+
onUnsnap(event: SnapEvent): void {
115+
console.log(event);
116+
this.snapped.set(false);
117+
}
118+
}

0 commit comments

Comments
 (0)