Skip to content

Commit

Permalink
feat: add multi line support
Browse files Browse the repository at this point in the history
  • Loading branch information
fredj committed Apr 22, 2024
1 parent f00b55e commit dbf73b9
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 24 deletions.
8 changes: 8 additions & 0 deletions demos/simple/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ function main() {
}
trackManager.addPOI(poiOverlay, onAddListener)
});

document.querySelector('#createNewPart').addEventListener('click', () => {
trackManager.createNewPart();
});
document.querySelector('#changeActivePart').addEventListener('click', () => {
const nextPart = (trackManager.activePart() + 1) % trackManager.partsCount();
trackManager.workOnPart(nextPart);
});
}

main();
7 changes: 6 additions & 1 deletion demos/simple/simple.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@
<button id="poiCancel">Cancel</button>
<button id="poiSave">Save</button>
</div>
</div>
<br />
<br />
<a href="#" id="createNewPart">Add a new line string</a>
<br />
<a href="#" id="changeActivePart">Change active line string</a>
</div>
<div id="map"></div>
</main>
</body>
Expand Down
6 changes: 4 additions & 2 deletions demos/simple/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export const sketchControlPoint = {
export const trackLine = {
"stroke-width": 6,
"stroke-color": "purple",
"text-value": ["concat", "", ["get", "part"]],
"text-fill-color": "#fff",
};

export const trackLineModifying = {
Expand All @@ -27,15 +29,15 @@ export const poiPoint = {
"text-font": "bold 11px Inter",
"text-fill-color": "#000",
// use 'concat' to convert number to string
"text-value": ["concat", ["get", "index"], ""],
"text-value": ["concat", "", ["get", "part"]],
};

export const numberedControlPoint = {
...controlPoint,
"circle-fill-color": "#ffffffdd",
"text-color": "blue",
// use 'concat' to convert number to string
"text-value": ["concat", ["get", "index"], ""],
"text-value": ["concat", "", ["get", "part"]],
};

export const snappedTrue = {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@geoblocks/edittrack",
"version": "1.3.8",
"version": "2.0.0-beta.4",
"description": "Geoblocks edittrack",
"scripts": {
"eslint": "eslint src test demos",
Expand Down
8 changes: 8 additions & 0 deletions src/interaction/TrackData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,18 @@ interface DeletedControlPoint {
}

export default class TrackData {
private part_: number;
private segments: Feature<LineString>[] = [];
private controlPoints: Feature<Point>[] = [];
private pois: Feature<Point>[] = [];

constructor(part: number) {
this.part_ = part;
}
get part() {
return this.part_;
}

parseFeatures(features: Feature<Point|LineString>[]): ParsedFeatures {
const parsed: ParsedFeatures = {
segments: [],
Expand Down
10 changes: 6 additions & 4 deletions src/interaction/TrackInteraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {Point} from 'ol/geom';
export interface Options {
map: Map;
trackLayer: VectorLayer<VectorSource>
trackData: TrackData
style: StyleLike | FlatStyleLike

/**
Expand Down Expand Up @@ -62,6 +61,10 @@ export default class TrackInteraction extends Interaction {
private modifyTrack_: Modify;
private deletePoint_: Select;

setTrackData(trackData: TrackData) {
this.modifyTrack_.setTrackData(trackData);
}

controlPointOrPOIAtPixel(pixel: Pixel): Feature<Point>|false {
return this.getMap().forEachFeatureAtPixel(pixel,
(f) => {
Expand All @@ -86,9 +89,8 @@ export default class TrackInteraction extends Interaction {
return draw;
}

createModifyInteraction(trackData: TrackData, source: VectorSource, style: StyleLike | FlatStyleLike, hitTolerance: number): Modify {
createModifyInteraction(source: VectorSource, style: StyleLike | FlatStyleLike, hitTolerance: number): Modify {
const modify = new Modify({
trackData: trackData,
source: source,
style: style,
condition: (event) => !this.deleteCondition_(event),
Expand Down Expand Up @@ -146,7 +148,7 @@ export default class TrackInteraction extends Interaction {
source.on('removefeature', () => requestAnimationFrame(() => this.modifyTrack_.updateSketchFeature()));

this.drawTrack_ = this.createDrawInteraction(source);
this.modifyTrack_ = this.createModifyInteraction(options.trackData, source, options.style, options.hitTolerance);
this.modifyTrack_ = this.createModifyInteraction(source, options.style, options.hitTolerance);
this.deletePoint_ = this.createSelectInteraction(options.trackLayer);

this.setActive(false);
Expand Down
22 changes: 18 additions & 4 deletions src/interaction/TrackInteractionModify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import LineString from 'ol/geom/LineString.js';
import Point from 'ol/geom/Point.js';
import Event from 'ol/events/Event.js';
import {Geometry} from 'ol/geom';
import TrackData from './TrackData';
import type TrackData from './TrackData';
import {Map, MapBrowserEvent} from 'ol';
import type {StyleLike} from 'ol/style/Style.js';
import type {FlatStyleLike} from 'ol/style/flat.js';
Expand All @@ -32,7 +32,6 @@ export class ModifyEvent extends Event {

export interface Options {
source: VectorSource<FeatureLike>;
trackData: TrackData;
style: StyleLike | FlatStyleLike;
condition: (mbe: MapBrowserEvent<UIEvent>) => boolean;
addControlPointCondition: (mbe: MapBrowserEvent<UIEvent>) => boolean;
Expand Down Expand Up @@ -65,7 +64,7 @@ export default class Modify extends PointerInteraction {
});
private overlay_: VectorLayer<VectorSource<Feature>>;
private lastPixel_: Pixel = [0, 0];
private trackData_: Options['trackData'];
private trackData_: TrackData;
private pointAtCursorFeature_ = new Feature<Point>({
geometry: new Point([0, 0]),
type: 'sketch',
Expand Down Expand Up @@ -103,10 +102,16 @@ export default class Modify extends PointerInteraction {
updateWhileAnimating: true,
updateWhileInteracting: true
});
}

this.trackData_ = options.trackData;
setTrackData(trackData: TrackData) {
this.trackData_ = trackData;
this.overlayFeature.set('part', this.trackData_.part);
}

featureInTrackData(feature: Feature<Geometry> | undefined) {
return feature?.get('part') === this.trackData_.part;
}

setMap(map: Map) {
this.overlay_.setMap(map);
Expand Down Expand Up @@ -177,6 +182,11 @@ export default class Modify extends PointerInteraction {

handleEvent(event: MapBrowserEvent<UIEvent>): boolean {
const stop = super.handleEvent(event);
// const feature = this.getFeatureAtPixel(event.pixel);
// if (feature?.get('part') !== this.trackData_.part) {
// return false;
// }

if (this.addControlPointCondition_(event)) {
const feature = this.getFeatureAtPixel(event.pixel);
if (feature && feature.get('type') === 'segment') {
Expand All @@ -197,6 +207,10 @@ export default class Modify extends PointerInteraction {
if (!this.feature_) {
return false;
}
if (!this.featureInTrackData(this.feature_)) {
this.feature_ = null;
return false;
}
this.dragStarted = false;
return true;
}
Expand Down
62 changes: 54 additions & 8 deletions src/interaction/TrackManager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// FIXME: move pois outside of track data

import Feature from 'ol/Feature.js';
import Point from 'ol/geom/Point.js';

Expand Down Expand Up @@ -73,7 +75,7 @@ export default class TrackManager<POIMeta> {
private trackChangeEventListeners_: Function[] = [];
// eslint-disable-next-line @typescript-eslint/ban-types
private trackHoverEventListeners_: Function[] = [];
private trackData_ = new TrackData();
private trackData_: TrackData;
private router_: Router;
get router(): Router {
return this.router_;
Expand All @@ -85,6 +87,7 @@ export default class TrackManager<POIMeta> {
private updater_: TrackUpdater;
private interaction_: TrackInteraction;
private historyManager_ = new HistoryManager<Feature<Point|LineString>[]>();
private parts: TrackData[] = [];

constructor(options: Options) {
this.map_ = options.map;
Expand All @@ -96,15 +99,14 @@ export default class TrackManager<POIMeta> {

this.router_ = options.router;
this.profiler_ = options.profiler;

this.updater_ = new TrackUpdater({
profiler: this.profiler_,
router: this.router_,
trackData: this.trackData_
});

this.interaction_ = new TrackInteraction({
style: options.style,
trackData: this.trackData_,
trackLayer: this.trackLayer_,
map: this.map_,
deleteCondition: options.deleteCondition,
Expand All @@ -113,6 +115,8 @@ export default class TrackManager<POIMeta> {
hitTolerance: this.hitTolerance_,
});

this.createNewPart();

// Hack to test profile synchro
// this.closestPointGeom_ = new Point([0, 0]);
// this.interaction_.modifyTrack_.overlay_.getSource().addFeature(new Feature({
Expand All @@ -131,9 +135,12 @@ export default class TrackManager<POIMeta> {
if (!this.snapping) {
feature.set('snapped', false);
}
feature.set('part', this.trackData_.part);
// this is what we want: the new point is added to the current part
const {pointFrom, pointTo, segment} = this.trackData_.pushControlPoint(feature);
if (segment) {
this.source_.addFeature(segment);
segment.set('part', this.trackData_.part);
await this.router_.snapSegment(segment, pointFrom, pointTo);
this.updater_.equalizeCoordinates(pointFrom);
await this.profiler_.computeProfile(segment);
Expand All @@ -145,6 +152,7 @@ export default class TrackManager<POIMeta> {

const debouncedMapToProfileUpdater = debounce(
(coordinate: Coordinate, hover: boolean) => {
// FIXME multi lines: check this
if (hover && this.trackData_.getSegments().length > 0) {
const segments = this.trackData_.getSegments().map(feature => feature.get('profile'));
const best = findClosestPointInLines(segments, coordinate, {tolerance: 1, interpolate: true});
Expand Down Expand Up @@ -176,22 +184,26 @@ export default class TrackManager<POIMeta> {
const type = event.feature.get('type') as FeatureType;

if (type === 'POI') {
this.trackData_.updatePOIIndexes();
// FIXME multi lines: check this
this.trackData_.updatePOIIndexes();
this.onTrackChanged_();
} else if (type === 'controlPoint') {
const feature = event.feature as Feature<Point>;
await this.updater_.updateAdjacentSegmentsGeometries(feature, this.snapping);
this.updater_.changeAdjacentSegmentsStyling(feature, '');
await this.updater_.computeAdjacentSegmentsProfile(feature);
// FIXME multi lines: check this
this.trackData_.updatePOIIndexes();
this.onTrackChanged_();
} else if (type === 'segment') {
const feature = event.feature as Feature<LineString>;
// FIXME multi lines: check this
const indexOfSegment = this.trackData_.getSegments().indexOf(feature);

console.assert(indexOfSegment >= 0);
const controlPoint = new Feature({
geometry: new Point(event.coordinate)
geometry: new Point(event.coordinate),
part: this.trackData_.part,
});
this.source_.addFeature(controlPoint);
const removed = this.trackData_.insertControlPointAt(controlPoint, indexOfSegment + 1);
Expand All @@ -200,11 +212,14 @@ export default class TrackManager<POIMeta> {

const {before, after} = this.trackData_.getAdjacentSegments(controlPoint);
console.assert(!!before && !!after);
before.set('part', this.trackData_.part);
after.set('part', this.trackData_.part);
this.source_.addFeatures([before, after]);

await this.updater_.updateAdjacentSegmentsGeometries(controlPoint, this.snapping);
this.updater_.changeAdjacentSegmentsStyling(controlPoint, '');
await this.updater_.computeAdjacentSegmentsProfile(controlPoint);
// FIXME multi lines: check this
this.trackData_.updatePOIIndexes();
this.onTrackChanged_();
}
Expand All @@ -220,11 +235,13 @@ export default class TrackManager<POIMeta> {
console.assert(selected.getGeometry().getType() === 'Point');
const type = selected.get('type') as FeatureType;
if (type === 'POI') {
// FIXME multi lines: check this
this.trackData_.deletePOI(selected);
this.source_.removeFeature(selected);
this.onTrackChanged_();
} else {
// control point
// FIXME multi lines: check this
const {deleted, pointBefore, pointAfter, newSegment} = this.trackData_.deleteControlPoint(selected);

// remove deleted features from source
Expand Down Expand Up @@ -329,7 +346,8 @@ export default class TrackManager<POIMeta> {

deleteLastPoint() {
if (this.mode_) {
if (this.trackData_.getControlPoints().length > 0) {
// FIXME multi lines: check this
if (this.trackData_.getControlPoints().length > 0) {
const deletedFeatures = this.trackData_.deleteLastControlPoint();
deletedFeatures.forEach(feature => this.source_.removeFeature(feature));
this.onTrackChanged_();
Expand Down Expand Up @@ -362,6 +380,7 @@ export default class TrackManager<POIMeta> {
private clearInternal_() {
this.source_.clear();
this.trackData_.clear();
// FIXME multi lines: remove all parts ?
}

/**
Expand All @@ -386,7 +405,6 @@ export default class TrackManager<POIMeta> {
}

async restoreFeatures(features: Feature<Point|LineString>[]): Promise<void> {
this.clearInternal_();
await this.restoreFeaturesInternal_(features);
this.onTrackChanged_();
}
Expand Down Expand Up @@ -572,4 +590,32 @@ export default class TrackManager<POIMeta> {
this.source_.changed();
this.shadowTrackLayer_.getSource().changed();
}
}

createNewPart(): number {
this.trackData_ = new TrackData(this.parts.length);
this.parts.push(this.trackData_);
this.updater_.setTrackData(this.trackData_);
this.interaction_.setTrackData(this.trackData_);

return this.trackData_.part;
}

activePart(): number {
return this.trackData_.part;
}

partsCount(): number {
return this.parts.length;
}

workOnPart(index: number) {
this.trackData_ = this.parts[index];
this.updater_.setTrackData(this.trackData_);
this.interaction_.setTrackData(this.trackData_);
}

getParts(): TrackData[] {
return this.parts;
}

}
Loading

0 comments on commit dbf73b9

Please sign in to comment.