Skip to content

Commit

Permalink
[feat] Add ruler customization options (#38)
Browse files Browse the repository at this point in the history
* [feat] Add ruler customization options

* [fix] Fix issues from PR

* doc strings

* fix tests

---------

Co-authored-by: Michel Beloshitsky <[email protected]>
  • Loading branch information
xalvaine and itanka9 authored Sep 24, 2024
1 parent a514ec4 commit 8613a01
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 50 deletions.
29 changes: 28 additions & 1 deletion src/control/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,26 @@ export interface RulerControlOptions extends mapgl.ControlOptions {
*/
mode?: RulerOptions['mode'];

/**
* Polygon drawing options.
*/
polygonOptions?: RulerOptions['polygonOptions'];

/**
* Polyline drawing options.
*/
polylineOptions?: RulerOptions['polylineOptions'];

/**
* Custom joint factory function, useful for styling
*/
jointFactory?: RulerOptions['jointFactory'];

/**
* Custom snap point factory function, useful for styling
*/
snapPointFactory?: RulerOptions['snapPointFactory'];

/**
* Specifies whether the ruler should be enabled after control initialization.
*/
Expand Down Expand Up @@ -52,7 +72,14 @@ export class RulerControl extends Control {
default:
throw new Error(`unsupported mode: ${mode}`);
}
this.ruler = new Ruler(this.map, { enabled: this.isEnabled, mode });
this.ruler = new Ruler(this.map, {
enabled: this.isEnabled,
mode,
polygonOptions: options.polygonOptions,
polylineOptions: options.polylineOptions,
jointFactory: options.jointFactory,
snapPointFactory: options.snapPointFactory,
});

this.render();
}
Expand Down
24 changes: 15 additions & 9 deletions src/joint.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GeoPoint, TargetedEvent } from './types';
import { GeoPoint, JointFactory, TargetedEvent } from './types';
import {
createHtmlMarker,
getJointDistanceText,
Expand Down Expand Up @@ -43,6 +43,11 @@ export class Joint extends Evented<EventTable> {
distance: number,
enableOnInit,
private showLabel: boolean,
private renderCustomMarker: JointFactory = (
map,
coordinates,
{ isFirst, interactive },
) => createHtmlMarker(map, coordinates, { big: isFirst, interactive }),
) {
super();
this.id = ++lastId;
Expand Down Expand Up @@ -75,10 +80,7 @@ export class Joint extends Evented<EventTable> {
}

enable() {
this.marker = createHtmlMarker(this.map, this.getCoordinates(), {
big: this.isFirst,
interactive: true,
});
this.createHtmlMarker();
this.addMarkerEventListeners();

this.updateLabel();
Expand Down Expand Up @@ -116,10 +118,7 @@ export class Joint extends Evented<EventTable> {
this.marker?.destroy();
document.removeEventListener('mouseup', this.onMouseUp);

this.marker = createHtmlMarker(this.map, this.coordinates, {
big: this.isFirst,
interactive: true,
});
this.createHtmlMarker();
this.addMarkerEventListeners();
this.setDistance(0, true);
}
Expand Down Expand Up @@ -240,6 +239,13 @@ export class Joint extends Evented<EventTable> {
targetData: this,
});
};

private createHtmlMarker = () => {
this.marker = this.renderCustomMarker(this.map, this.getCoordinates(), {
isFirst: this.isFirst,
interactive: true,
});
};
}

function createLabel(map: mapgl.Map, coordinates: GeoPoint, distance: number, isFirst: boolean) {
Expand Down
24 changes: 17 additions & 7 deletions src/polygon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { geoPointsDistance } from './utils';
import { Joint } from './joint';
import { area } from './geo/area';
import { centroid } from './geo/centroid';
import { GeoPoint } from './types';
import { GeoPoint, RulerPolygonOptions } from './types';
import { style } from './style';
import { getTranslation } from './l10n';
import { PolygonOptions } from '@2gis/mapgl/types/types';

/**
* @internal
Expand All @@ -17,20 +18,25 @@ export class Polygon {
private area: number;
private perimeter: number;

constructor(private readonly map: mapgl.Map, joints: Joint[], private showLabel: boolean) {
constructor(
private readonly map: mapgl.Map,
joints: Joint[],
private showLabel: boolean,
options: Pick<PolygonOptions, 'color' | 'strokeColor' | 'strokeWidth'>,
) {
this.area = 0;
this.perimeter = 0;
this.centroid = [0, 0];

if (joints.length > 2) {
const points = joints.map((j) => j.getCoordinates());
this.polygon = createPolygon(this.map, points);
this.polygon = createPolygon(this.map, points, options);
this.centroid = centroid(points);
this.updateLabel();
}
}

update(joints: Joint[]) {
update(joints: Joint[], options: RulerPolygonOptions) {
this.polygon?.destroy();
if (joints.length <= 2) {
this.perimeter = 0;
Expand All @@ -47,7 +53,7 @@ export class Polygon {
geoPointsDistance(points[points.length - 1], points[0]);

this.area = area(points);
this.polygon = createPolygon(this.map, points);
this.polygon = createPolygon(this.map, points, options);
this.updateLabel();
}

Expand Down Expand Up @@ -98,12 +104,16 @@ function createLabel(map: mapgl.Map, coordinates: GeoPoint, area: number): mapgl
});
}

function createPolygon(map: mapgl.Map, points: GeoPoint[]): mapgl.Polygon {
function createPolygon(
map: mapgl.Map,
points: GeoPoint[],
options: RulerPolygonOptions,
): mapgl.Polygon {
return new mapgl.Polygon(map, {
coordinates: [points],
zIndex: style.areaPhase,
interactive: false,
color: style.areaColor,
color: options.color ?? style.areaColor,
strokeWidth: style.areaStrokeWidth,
});
}
12 changes: 8 additions & 4 deletions src/polyline.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GeoPoint, RulerMode } from './types';
import { GeoPoint, RulerMode, RulerPolylineOptions } from './types';
import { createLine, geoPointsDistance } from './utils';
import { Joint } from './joint';
import { Evented } from './evented';
Expand All @@ -21,7 +21,11 @@ export class Polyline extends Evented<EventTable> {
super();
}

update(mode: RulerMode, joints: Joint[]) {
update(mode: RulerMode, joints: Joint[], polylineOptions: RulerPolylineOptions) {
if (polylineOptions.autoClosePolygon === undefined) {
polylineOptions.autoClosePolygon = true;
}

this.polyline?.destroy();
if (joints.length === 0) {
return;
Expand All @@ -41,11 +45,11 @@ export class Polyline extends Evented<EventTable> {
});

// замыкаем линию если рисуем площадь
if (mode === 'polygon') {
if (mode === 'polygon' && polylineOptions.autoClosePolygon) {
points.push(joints[0].getCoordinates());
}

this.polyline = createLine(this.map, points, false);
this.polyline = createLine(this.map, points, false, polylineOptions);
this.polyline.on('mousemove', (ev) => this.emit('mousemove', ev));
this.polyline.on('mouseout', (ev) => this.emit('mouseout', ev));
this.polyline.on('click', (ev) => this.emit('click', ev));
Expand Down
6 changes: 3 additions & 3 deletions src/previewLine.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Joint } from './joint';
import { GeoPoint, RulerMode } from './types';
import { GeoPoint, RulerMode, RulerPolylineOptions } from './types';
import { createLine } from './utils';

/**
Expand Down Expand Up @@ -28,7 +28,7 @@ export class PreviewLine {
this.draggableJoint = joint;
}

update(mode: RulerMode, joints: Joint[]) {
update(mode: RulerMode, joints: Joint[], previewLineOptions: RulerPolylineOptions) {
this.polyline?.destroy();
if (this.draggableJoint) {
const curr = joints.indexOf(this.draggableJoint);
Expand All @@ -50,7 +50,7 @@ export class PreviewLine {
coordinates.push((joints[curr + 1] ?? joints[0]).getCoordinates());
}

this.polyline = createLine(this.map, coordinates, true);
this.polyline = createLine(this.map, coordinates, true, previewLineOptions);
}
}
}
36 changes: 31 additions & 5 deletions src/ruler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import {
TargetedEvent,
RulerData,
RulerMode,
RulerPolygonOptions,
RulerPolylineOptions,
JointFactory,
SnapPointFactory,
} from './types';
import { geoPointsDistance, getSnapPoint } from './utils';
import { Joint } from './joint';
Expand Down Expand Up @@ -73,6 +77,14 @@ export interface RulerOptions {
* Specifies whether the labels should be drawn. Optional.
*/
labelVisibilitySettings?: LabelVisibilitySettings;

polygonOptions?: RulerPolygonOptions;

polylineOptions?: RulerPolylineOptions;

jointFactory?: JointFactory;

snapPointFactory?: SnapPointFactory;
}

interface RedrawFlags {
Expand All @@ -98,6 +110,10 @@ export class Ruler extends Evented<RulerEventTable> {
private polyline: Polyline;
private newSnapInfo?: SnapInfo;
private polygon?: Polygon;
private polygonOptions: RulerPolygonOptions;
private polylineOptions: RulerPolylineOptions;
private jointFactory?: RulerOptions['jointFactory'];
private snapPointFactory?: RulerOptions['snapPointFactory'];

/**
* Example:
Expand Down Expand Up @@ -137,6 +153,10 @@ export class Ruler extends Evented<RulerEventTable> {
this.polyline.on('mousemove', this.onPolylineMouseMove);
this.polyline.on('mouseout', this.onPolylineMouseOut);
this.polyline.on('click', this.onPolylineClick);
this.polygonOptions = options.polygonOptions ?? {};
this.polylineOptions = options.polylineOptions ?? {};
this.jointFactory = options.jointFactory;
this.snapPointFactory = options.snapPointFactory;

options.points?.forEach((point, i) => this.addPoint(point, i));

Expand Down Expand Up @@ -166,7 +186,12 @@ export class Ruler extends Evented<RulerEventTable> {
this.redrawFlags.polyline = true;

if (this.mode === 'polygon') {
this.polygon = new Polygon(this.map, this.joints, this.labelVisibilitySettings.area);
this.polygon = new Polygon(
this.map,
this.joints,
this.labelVisibilitySettings.area,
this.polygonOptions,
);
}

this.map.on('click', this.onClick);
Expand Down Expand Up @@ -278,6 +303,7 @@ export class Ruler extends Evented<RulerEventTable> {
distance,
this.enabled,
this.labelVisibilitySettings.perimeter,
this.jointFactory,
);

joint.on('dragstart', this.onJointMoveStart);
Expand Down Expand Up @@ -447,18 +473,18 @@ export class Ruler extends Evented<RulerEventTable> {

if (this.redrawFlags.polyline) {
this.redrawFlags.polyline = false;
this.polygon?.update(this.joints);
this.polyline.update(this.mode, this.joints);
this.polygon?.update(this.joints, this.polygonOptions);
this.polyline.update(this.mode, this.joints, this.polylineOptions);
}

if (this.redrawFlags.preview) {
this.redrawFlags.preview = false;
this.previewLine.update(this.mode, this.joints);
this.previewLine.update(this.mode, this.joints, this.polylineOptions);
}

if (this.redrawFlags.snap) {
this.redrawFlags.snap = false;
this.snapPoint.update(this.newSnapInfo);
this.snapPoint.update(this.newSnapInfo, this.snapPointFactory);
this.newSnapInfo = undefined;
}

Expand Down
13 changes: 7 additions & 6 deletions src/snapPoint.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GeoPoint, SnapInfo } from './types';
import { GeoPoint, SnapPointFactory, SnapInfo } from './types';
import { createHtmlMarker, getJointDistanceText, getLabelHtml, getSnapLabelHtml } from './utils';
import { style } from './style';
import { getTranslation } from './l10n';
Expand All @@ -15,7 +15,11 @@ export class SnapPoint {

constructor(private readonly map: mapgl.Map, private showLabel: boolean) {}

update(info: SnapInfo | undefined) {
update(
info: SnapInfo | undefined,
snapPointFactory: SnapPointFactory = (map, coordinates) =>
createHtmlMarker(map, coordinates, { big: true, interactive: false }),
) {
if (info === undefined) {
this.destroy();
return;
Expand All @@ -24,10 +28,7 @@ export class SnapPoint {
this.distance = info.distance;

if (!this.marker) {
this.marker = createHtmlMarker(this.map, this.point, {
big: true,
interactive: false,
});
this.marker = snapPointFactory(this.map, this.point);
} else {
this.marker.setCoordinates(this.point);
}
Expand Down
23 changes: 23 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { PolygonOptions } from '@2gis/mapgl/types/types';

/**
* Possible modes of operation of the plugin.
*/
Expand Down Expand Up @@ -60,3 +62,24 @@ export interface PolylineData extends BaseData {
}

export type RulerData = PolygonData | PolylineData;

export type RulerPolygonOptions = Pick<PolygonOptions, 'color'>;

export interface RulerPolylineOptions {
lineColor?: string;
lineWidth?: number;
lineBorderColor?: string;
lineBorderWidth?: number;
lineBorder2Color?: string;
lineBorder2Width?: number;
previewLineColor?: string;
autoClosePolygon?: boolean;
}

export type JointFactory = (
map: mapgl.Map,
coordinates: GeoPoint,
options: { isFirst: boolean; interactive: boolean },
) => mapgl.HtmlMarker;

export type SnapPointFactory = (map: mapgl.Map, coordinates: GeoPoint) => mapgl.HtmlMarker;
Loading

0 comments on commit 8613a01

Please sign in to comment.