Skip to content

Commit

Permalink
0.3.0 - Added events dispatching (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
ponlawat-w authored Nov 28, 2023
1 parent 260dfc4 commit 9a13219
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 23 deletions.
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,20 @@ map.addInteraction(interaction);

If `waySource` is not provided, `OSMOverpass` will be used as source for snapping, so the constructor options for `OSMWaySnap` will be extended to include [thoses options from `OSMOverpassSourceBase`](https://github.com/ponlawat-w/ol-osmoverpass#constructor-options).

## Events

All event objects dispatched have property `feature` being the active feature of the interaction.

Event types:
- `waysnapstart`: when the interaction starts on a feature whether creation or edition.
- `waysnapstartcreate`: when the interaction has created a new feature.
- `waysnapstartedit`: when the interaction has started edition an existing feature.
- `waysnapupdate`: when the interaction has updated the active feature.
- `waysnapend`: when interaction has finished.

## Examples

[Full page example using the library from CDN](./examples/index.html)
[Examples in HTML using CDN](./examples/index.html)

### Using as module

Expand Down Expand Up @@ -110,8 +121,4 @@ const interaction = new OSMWaySnap({
map.addInteraction(interaction);
```

### Using as CDN

[HTML Example](./examples/index.html)

---
23 changes: 20 additions & 3 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@
</div>
</div>

<div>
<span id="status"></span>
|
<span id="event"></span>
</div>

<div id="status"></div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ol.js"></script>
<script src="https://www.unpkg.com/ol-osmwaysnap/dist/bundle/index.js"></script>
<script lang="js">
Expand Down Expand Up @@ -76,12 +78,27 @@
document.getElementById('status').innerHTML = 'Loading from OSM…';
});
interaction.getWaySource().on('featuresloadend', () => {
document.getElementById('status').innerHTML = '';
document.getElementById('status').innerHTML = 'Idle';
});
interaction.getWaySource().on('featuresloaderror', () => {
document.getElementById('status').innerHTML = 'ERROR';
});

interaction.on('waysnapstartcreate', () => {
document.getElementById('event').innerHTML = 'A new feature has been created.';
});
interaction.on('waysnapstartedit', () => {
document.getElementById('event').innerHTML = 'A feature edition has started.';
});
interaction.on('waysnapupdate', e => {
const n = e.feature.getGeometry().getCoordinates().length;
document.getElementById('event').innerHTML = `The feature has been updated with vertices: ${n}`;
});
interaction.on('waysnapend', e => {
const n = e.feature.getGeometry().getCoordinates().length;
document.getElementById('event').innerHTML = `Feature edition ended with total vertices: ${n}`;
});

const wkt = new ol.format.WKT();
const geojson = new ol.format.GeoJSON();

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ol-osmwaysnap",
"type": "module",
"version": "0.2.0",
"version": "0.3.0",
"description": "OpenLayers Interaction Extension for Snapping Ways from OSM using Overpass API",
"keywords": [
"osm",
Expand Down
55 changes: 55 additions & 0 deletions src/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Event from 'ol/events/Event';
import type { Feature } from 'ol';
import type { ObjectEvent } from 'ol/Object';
import type { Types as ObjectEventTypes } from 'ol/ObjectEventType';
import type { CombinedOnSignature, EventTypes as ObservableEventTypes, OnSignature } from 'ol/Observable';
import type { LineString } from 'ol/geom';

/**
* Event type for OSMWaySnapInteraction
*/
export const OSMWaySnapEventType = {
/** Event when OSMWaySnap interaction starts on a feature whether creation or edition. */
WAYSNAPSTART: 'waysnapstart',

/** Event when OSMWaySnap interaction has created a new feature. */
WAYSNAPSTARTCREATE: 'waysnapstartcreate',

/** Event when OSMWaySnap has started edition an existing feature. */
WAYSNAPSTARTEDIT: 'waysnapstartedit',

/** Event when OSMWaySnap has updated the active feature. */
WAYSNAPUPDATE: 'waysnapupdate',

/** Event when OSMWaySnap has finished. */
WAYSNAPEND: 'waysnapend'
} as const;

type OSMWaySnapEventType = typeof OSMWaySnapEventType[keyof typeof OSMWaySnapEventType];

/**
* Event for OSMWaySnap interaction
*/
export class OSMWaySnapEvent extends Event {
/** Active feature for the event */
private _feature: Feature<LineString>;

/** Active feature for the event */
public get feature(): Feature<LineString> { return this._feature };

/**
* Constructor
* @param type Type of event
* @param feature Active feature for the event
*/
constructor(type: OSMWaySnapEventType, feature: Feature<LineString>) {
super(type);
this._feature = feature;
}
}

/** Type signature for OSMWaySnap interaction event dispatcher */
export type OSMWaySnapOnSignature<Return> = OnSignature<ObservableEventTypes, Event, Return>
& OnSignature<ObjectEventTypes|'change:active', ObjectEvent, Return>
& OnSignature<OSMWaySnapEventType, OSMWaySnapEvent, Return>
& CombinedOnSignature<ObservableEventTypes|ObjectEventTypes|'change:active'|OSMWaySnapEventType, Return>;
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { OSMWaySnapEventType, OSMWaySnapEvent, type OSMWaySnapOnSignature } from './event';
export { default as OSMWaySnap, type OSMWaySnapOptions } from './interaction';
export { default as LineStringUtils } from './line-string-utils';
62 changes: 57 additions & 5 deletions src/interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import { Vector as VectorSource } from 'ol/source';
import { Circle, Fill, Stroke } from 'ol/style';
import Style, { createEditingStyle } from 'ol/style/Style';
import { OSMOverpassWaySource, type OSMOverpassSourceOptions } from 'ol-osmoverpass';
import { OSMWaySnapEvent, OSMWaySnapEventType } from './event';
import LineStringUtils from './line-string-utils';
import type { MapBrowserEvent } from 'ol';
import type { EventsKey } from 'ol/events';
import type { Coordinate } from 'ol/coordinate';
import type { StyleLike } from 'ol/style/Style';
import type { MapBrowserEvent } from 'ol';
import type { OSMWaySnapOnSignature } from './event';

/** Options for OSMWaySnap interaction */
type SnapOptions = {
Expand Down Expand Up @@ -47,6 +50,7 @@ type SnapOptionsOSMOverpassWaySource = Omit<SnapOptions, 'waySource'> & {
waySource?: undefined
} & Partial<OSMOverpassSourceOptions>;

/** Options for OSMWaySnap interaction */
export type OSMWaySnapOptions = SnapOptions | SnapOptionsOSMOverpassWaySource;

/**
Expand Down Expand Up @@ -128,6 +132,10 @@ export default class OSMWaySnap extends PointerInteraction {
this.addChangeListener('active', this.activeChanged.bind(this));
}

declare on: OSMWaySnapOnSignature<EventsKey>;
declare once: OSMWaySnapOnSignature<EventsKey>;
declare un: OSMWaySnapOnSignature<EventsKey>;

/**
* Remove the interaction from its current map and attach it to the new map.
* Subclasses may set up event handlers to get notified about changes to
Expand Down Expand Up @@ -172,11 +180,14 @@ export default class OSMWaySnap extends PointerInteraction {
return this.activeFeature;
}

/** Called when the editing is finished, clear all the sketching and candidates. */
/**
* Called when the editing is finished, clear all the sketching and candidates.
*/
public finishEditing() {
if (this.autoFocus && this.map && this.activeFeature && this.coordinates.length > 1) {
this.map.getView().fit(this.activeFeature.getGeometry()!.getExtent(), { padding: Array(4).fill(this.focusPadding) });
}
const feature = this.activeFeature;
this.activeFeature = undefined;
this.coordinates = [];
this.removeSketchPoint();
Expand All @@ -188,6 +199,8 @@ export default class OSMWaySnap extends PointerInteraction {
}
this.draftOriginalLine = undefined;
this.mergingDraftOriginalLine = false;
this.updateOverlayLayer();
this.dispatchEnd(feature);
}

/**
Expand All @@ -204,13 +217,15 @@ export default class OSMWaySnap extends PointerInteraction {
const overlappedFeatures = this.source.getFeatures().filter(f => f.getGeometry()?.intersectsCoordinate(event.coordinate) ?? false);
if (overlappedFeatures.length) {
this.enterEditMode(overlappedFeatures[0], event.coordinate);
this.updateFeature();
this.updateFeature(false);
this.dispatchStartEdit(this.activeFeature!)
return false;
}
}
if (this.allowCreate) {
this.coordinates = [this.sketchPoint?.getGeometry()!.getCoordinates() ?? event.coordinate];
this.updateFeature();
this.updateFeature(false);
this.dispatchStartCreate(this.activeFeature!);
return false;
}
return super.handleEvent(event);
Expand Down Expand Up @@ -241,6 +256,40 @@ export default class OSMWaySnap extends PointerInteraction {
return super.handleMoveEvent(event);
}

/**
* Dispatch start and start create events
* @param feature Feature to dispatch event, otherwise this.activeFeature!
*/
protected dispatchStartCreate(feature?: Feature<LineString>) {
this.dispatchEvent(new OSMWaySnapEvent(OSMWaySnapEventType.WAYSNAPSTART, feature ?? this.activeFeature!));
this.dispatchEvent(new OSMWaySnapEvent(OSMWaySnapEventType.WAYSNAPSTARTCREATE, feature ?? this.activeFeature!));
}

/**
* Dispatch start and start edit events
* @param feature Feature to dispatch event, otherwise this.activeFeature!
*/
protected dispatchStartEdit(feature?: Feature<LineString>) {
this.dispatchEvent(new OSMWaySnapEvent(OSMWaySnapEventType.WAYSNAPSTART, feature ?? this.activeFeature!));
this.dispatchEvent(new OSMWaySnapEvent(OSMWaySnapEventType.WAYSNAPSTARTEDIT, feature ?? this.activeFeature!));
}

/**
* Dispatch update event
* @param feature Feature to dispatch event, otherwise this.activeFeature!
*/
protected dispatchUpdate(feature?: Feature<LineString>) {
this.dispatchEvent(new OSMWaySnapEvent(OSMWaySnapEventType.WAYSNAPUPDATE, feature ?? this.activeFeature!));
}

/**
* Disptach end event
* @param feature Feature to dispatch event, otherwise this.activeFeature!
*/
protected dispatchEnd(feature?: Feature<LineString>) {
this.dispatchEvent(new OSMWaySnapEvent(OSMWaySnapEventType.WAYSNAPEND, feature ?? this.activeFeature!));
}

/**
* Start editing on the selected feature
* @param feature Feature to edit
Expand All @@ -264,8 +313,9 @@ export default class OSMWaySnap extends PointerInteraction {

/**
* Create or update active feature from editing coordinates.
* @param dispatch True to dipatch waysnapupdate event
*/
private updateFeature() {
private updateFeature(dispatch: boolean = true) {
if (!this.activeFeature) {
this.activeFeature = new Feature<LineString>(new LineString(this.coordinates));
} else {
Expand All @@ -274,6 +324,7 @@ export default class OSMWaySnap extends PointerInteraction {
this.source.addFeature(this.activeFeature);
}
}
dispatch && this.dispatchUpdate();
this.calculateCandidates();
}

Expand Down Expand Up @@ -398,6 +449,7 @@ export default class OSMWaySnap extends PointerInteraction {
* @param coordinates Original sketch line coordinates to get extended
*/
private extendSketchLineCoorsToDraftOriginal(coordinates: Coordinate[]) {
this.mergingDraftOriginalLine = false;
const lastCoordinate = coordinates[coordinates.length - 1];
if (!this.draftOriginalLine || !this.draftOriginalLine.getGeometry()!.intersectsCoordinate(lastCoordinate)) return;
let lineToExtend = this.draftOriginalLine.getGeometry()!;
Expand Down
14 changes: 7 additions & 7 deletions tests/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import type { OSMWaySnap } from '../dist';
/**
* Drawing a linestring network that looks more or less like this:
*
* │
* ┌─┴─┐
* │ │
* └─┬─┘
* ──┼──
* │
* │
* │ => fLoopExtended
* ┌─┴─┐ => fLoop
* │ │ => fLoop
* └─┬─┘ => fCenterToBeforeLoop
* ──┼── => fCenterTurnLeft / fCenterTurnRight
* │ => fDownToCenter
* │ => fDownToCenter
* @returns vector source
*/
export const getDefaultWaySource = (): VectorSource<Feature<LineString>> => {
Expand Down
47 changes: 46 additions & 1 deletion tests/interaction.create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import { Projection } from 'ol/proj';
import { getDefaultWaySource, mouseClick, mouseMove } from './common';
import { OSMWaySnap } from '../dist';
import { OSMWaySnap, OSMWaySnapEventType } from '../dist';
import { Feature } from 'ol';
import { LineString, Point } from 'ol/geom';

Expand Down Expand Up @@ -317,4 +317,49 @@ describe('Test OSMWaySnap Interaction: Line Creation', () => {
expect(coors[1]).toEqual([25, 25]);
expect(coors[2]).toEqual([50, 25]);
});

it('fires events when start creating and finish creating features', () => {
let waySnapStart = false;
let waySnapStartCreate = false;
let waySnapStartEdit = false;
let waySnapUpdate = false;
let waySnapEnd = false;
let startFeature: Feature<LineString>|undefined = undefined;
let startCreateFeature: Feature<LineString>|undefined = undefined;
let startEditFeature: Feature<LineString>|undefined = undefined;
let updateFeature: Feature<LineString>|undefined = undefined;
let endFeature: Feature<LineString>|undefined = undefined;
interaction.on(OSMWaySnapEventType.WAYSNAPSTART, e => { waySnapStart = true; startFeature = e.feature; });
interaction.on(OSMWaySnapEventType.WAYSNAPSTARTCREATE, e => { waySnapStartCreate = true; startCreateFeature = e.feature; });
interaction.on(OSMWaySnapEventType.WAYSNAPSTARTEDIT, e => { waySnapStartEdit = true; startEditFeature = e.feature; });
interaction.on(OSMWaySnapEventType.WAYSNAPUPDATE, e => { waySnapUpdate = true; updateFeature = e.feature; });
interaction.on(OSMWaySnapEventType.WAYSNAPEND, e => { waySnapEnd = true; endFeature = e.feature; });

mouseMove(map, interaction, [0, 0]);
mouseClick(map, interaction, [0, 0]);

expect(waySnapStart).toBeTruthy();
expect(waySnapStartCreate).toBeTruthy();
expect(waySnapStartEdit).toBeFalsy();
expect(waySnapUpdate).toBeFalsy();
expect(waySnapEnd).toBeFalsy();
expect(startFeature).toBeDefined();
expect(startCreateFeature).toBeDefined();
expect(startEditFeature).toBeUndefined();
expect(updateFeature).toBeUndefined();
expect(endFeature).toBeUndefined();
expect(startCreateFeature).toBe(startFeature);

mouseMove(map, interaction, [50, 25]);
mouseClick(map, interaction, [50, 25]);

expect(waySnapUpdate).toBeTruthy();
expect(waySnapEnd).toBeFalsy();
expect(updateFeature).toBe(startCreateFeature);

mouseClick(map, interaction, [50, 25]);

expect(waySnapEnd).toBeTruthy();
expect(endFeature).toBe(startCreateFeature);
});
});
Loading

0 comments on commit 9a13219

Please sign in to comment.