diff --git a/src/components/dialogs/MapListDialog.tsx b/src/components/dialogs/MapListDialog.tsx index 6c4ae16..0924e77 100644 --- a/src/components/dialogs/MapListDialog.tsx +++ b/src/components/dialogs/MapListDialog.tsx @@ -76,7 +76,6 @@ class Component extends React.Component { - console.log('stop it!'); this.setState({ editor_target: null }); }; diff --git a/src/index.tsx b/src/index.tsx index 184a75a..0a5e939 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -14,10 +14,6 @@ todo polyline editing only in manual mode (or by click) todo selecting logo on crop - done public maps - done editing map on map list - done setting map public on map list - todo network operations notify done delayed notify (delay(2000).then(showLoadingMsg)) todo network error notifications @@ -28,6 +24,10 @@ ## DONE + done public maps + done editing map on map list + done setting map public on map list + done routing spinner done maybe: stickers clusterization? done moving out the screen makes stickers editable again diff --git a/src/redux/user/sagas.ts b/src/redux/user/sagas.ts index 600cf9b..12fbb06 100644 --- a/src/redux/user/sagas.ts +++ b/src/redux/user/sagas.ts @@ -41,6 +41,7 @@ import { MODES } from '$constants/modes'; import { DEFAULT_USER } from '$constants/auth'; import { TIPS } from '$constants/tips'; import { + composeArrows, composeImages, composePoly, composeStickers, downloadCanvas, fetchImages, @@ -362,6 +363,7 @@ function* getRenderData() { yield composeImages({ geometry, images, ctx }); yield composePoly({ points, ctx }); + yield composeArrows({ points, ctx }); yield composeStickers({ stickers, ctx }); yield put(setRenderer({ info: 'Готово', progress: 1 })); diff --git a/src/sprites/arrow.svg b/src/sprites/arrow.svg new file mode 100644 index 0000000..ea28122 --- /dev/null +++ b/src/sprites/arrow.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/utils/arrow.ts b/src/utils/arrow.ts index 7d7fa5a..e5439d8 100644 --- a/src/utils/arrow.ts +++ b/src/utils/arrow.ts @@ -1,5 +1,8 @@ import { divIcon, LatLngLiteral, Marker, marker, DivIcon } from "leaflet"; +const arrow_image = require('$sprites/arrow.svg'); + +// export const createArrow = (latlng: LatLngLiteral, angle: number): Marker => marker(latlng, { draggable: false, interactive: false, @@ -7,7 +10,7 @@ export const createArrow = (latlng: LatLngLiteral, angle: number): Marker => mar html: `
- +
`, diff --git a/src/utils/geom.ts b/src/utils/geom.ts index 11db158..7c21275 100644 --- a/src/utils/geom.ts +++ b/src/utils/geom.ts @@ -1,4 +1,4 @@ -import { LatLng, LatLngLiteral, Point, PointExpression } from "leaflet"; +import { LatLng, LatLngLiteral, point, Point, PointExpression } from "leaflet"; interface ILatLng { lat: number, @@ -10,9 +10,18 @@ export const middleCoord = (l1: ILatLng, l2: ILatLng): ILatLng => ({ lng: (l2.lng + ((l1.lng - l2.lng) / 2)) }); +export const middleCoordPx = (p1: Point, p2: Point): Point => point({ + x: (p1.x + ((p2.x - p1.x) / 2)), + y: (p1.y + ((p2.y - p1.y) / 2)) +}); + export const deg2rad = (deg: number): number => ((deg * Math.PI) / 180); export const rad2deg = (rad: number): number => ((rad / Math.PI) * 180); +export const findDistancePx = (p1: Point, p2: Point): number => { + return Math.sqrt(((p1.x - p2.x) ** 2) + ((p1.y - p2.y) ** 2)); +}; + export const findDistance = (t1: number, n1: number, t2: number, n2: number): number => { // convert coordinates to radians const lat1 = deg2rad(t1); diff --git a/src/utils/renderer.ts b/src/utils/renderer.ts index 9cdbd5c..4a2e1ee 100644 --- a/src/utils/renderer.ts +++ b/src/utils/renderer.ts @@ -6,11 +6,8 @@ import { STICKERS } from '$constants/stickers'; import { ILatLng } from "$modules/Stickers"; import { IStickerDump } from "$modules/Sticker"; import { IRootState } from "$redux/user/reducer"; - -export interface IMapPoint { - x: number, - y: number, -} +import { angleBetweenPoints, angleBetweenPointsRad, findDistancePx, middleCoordPx } from "$utils/geom"; +import { Point } from "leaflet"; export interface ITilePlacement { minX: number, @@ -86,7 +83,7 @@ export const getTilePlacement = (): ITilePlacement => { }; }; -export const getPolyPlacement = (): IMapPoint[] => ( +export const getPolyPlacement = (): Point[] => ( (!editor.poly.poly || !editor.poly.poly.getLatLngs() || editor.poly.poly.getLatLngs().length <= 0) ? [] : editor.poly.poly.getLatLngs().map((latlng) => ({ ...editor.map.map.latLngToContainerPoint(latlng) })) @@ -148,7 +145,7 @@ export const composeImages = ( }); }; -export const composePoly = ({ points, ctx }: { points: IMapPoint[], ctx: CanvasRenderingContext2D }): void => { +export const composePoly = ({ points, ctx }: { points: Point[], ctx: CanvasRenderingContext2D }): void => { if (editor.poly.isEmpty) return; let minX = points[0].x; @@ -183,6 +180,42 @@ export const composePoly = ({ points, ctx }: { points: IMapPoint[], ctx: CanvasR ctx.closePath(); }; +export const composeArrows = async ({ points, ctx }: { points: Point[], ctx: CanvasRenderingContext2D }): Promise => { + const image = await imageFetcher(require('$sprites/arrow.svg')); + + const distances = points.map((point, i) => ( + (points[i + 1] && findDistancePx(points[i], points[i + 1])) || 0 + )); + + // we want to annotate at least 5 arrows + const min_arrows = (distances.length >= 5 ? 5 : distances.length - 1); + const min_distance = distances.sort((a, b) => (b - a))[min_arrows]; + + return points.map((point, i) => { + if (!points[i + 1]) return false; + + const distance = findDistancePx(points[i], points[i + 1]); + const angle = angleBetweenPointsRad(points[i], points[i + 1]); + + if (distance < min_distance && distance < 100) return false; + + const middle = middleCoordPx(points[i], points[i + 1]); + + ctx.save(); + ctx.translate(middle.x, middle.y); + ctx.rotate((Math.PI * 0.5) - angle); + ctx.translate(-middle.x, -middle.y); + + ctx.moveTo(middle.x, middle.y); + + ctx.drawImage(image, middle.x - 24, middle.y - 24, 48, 48); + + ctx.restore(); + + return true; + }); +}; + const measureText = ( ctx: CanvasRenderingContext2D, text: string,