dist and time

finishing route
This commit is contained in:
muerwre 2018-08-24 12:36:36 +07:00
parent 102328a1b2
commit b8545105c8
15 changed files with 195 additions and 37 deletions

8
package-lock.json generated
View file

@ -7629,6 +7629,14 @@
"resolved": "https://registry.npmjs.org/leaflet-editable/-/leaflet-editable-1.1.0.tgz", "resolved": "https://registry.npmjs.org/leaflet-editable/-/leaflet-editable-1.1.0.tgz",
"integrity": "sha1-93dZekCoGic/KHtIn9D+XM1gyNA=" "integrity": "sha1-93dZekCoGic/KHtIn9D+XM1gyNA="
}, },
"leaflet-geometryutil": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/leaflet-geometryutil/-/leaflet-geometryutil-0.9.0.tgz",
"integrity": "sha512-xlie+JCF+zJKc8fujgIUFnob5roEz4GTiZ8GaDR6O24g3I2nsOcCc4GQUgKGpnDMOV4BKa7QavKxy/d92bLyYw==",
"requires": {
"leaflet": ">=0.7.0"
}
},
"leaflet-routing-machine": { "leaflet-routing-machine": {
"version": "github:muerwre/leaflet-routing-machine#4be3c24db31b7c2e9750f5894e538969e953a093", "version": "github:muerwre/leaflet-routing-machine#4be3c24db31b7c2e9750f5894e538969e953a093",
"from": "github:muerwre/leaflet-routing-machine#no-osrm-text", "from": "github:muerwre/leaflet-routing-machine#no-osrm-text",

View file

@ -53,6 +53,7 @@
"history": "^4.7.2", "history": "^4.7.2",
"leaflet": "^1.3.3", "leaflet": "^1.3.3",
"leaflet-editable": "^1.1.0", "leaflet-editable": "^1.1.0",
"leaflet-geometryutil": "^0.9.0",
"leaflet-routing-machine": "muerwre/leaflet-routing-machine#no-osrm-text", "leaflet-routing-machine": "muerwre/leaflet-routing-machine#no-osrm-text",
"less": "^3.8.1", "less": "^3.8.1",
"lodash": "^4.17.10", "lodash": "^4.17.10",

View file

@ -3,12 +3,13 @@ import { MODES } from '$constants/modes';
import { RouterHelper } from '$components/router/RouterHelper'; import { RouterHelper } from '$components/router/RouterHelper';
export const EditorDialog = ({ mode, routerPoints }) => { export const EditorDialog = ({ mode, routerPoints, editor }) => {
const showDialog = (mode === MODES.ROUTER); const showDialog = (mode === MODES.ROUTER);
return ( return (
showDialog && showDialog &&
<div id="control-dialog"> <div id="control-dialog">
{ mode === MODES.ROUTER && <RouterHelper routerPoints={routerPoints} /> } { mode === MODES.ROUTER && <RouterHelper routerPoints={routerPoints} editor={editor} /> }
</div> </div>
); );
}; };

View file

@ -2,6 +2,8 @@ import React from 'react';
import { MODES } from '$constants/modes'; import { MODES } from '$constants/modes';
import classnames from 'classnames'; import classnames from 'classnames';
import { toHours } from '$utils/time';
import { Icon } from '$components/panels/Icon'; import { Icon } from '$components/panels/Icon';
import { EditorDialog } from '$components/panels/EditorDialog'; import { EditorDialog } from '$components/panels/EditorDialog';
@ -15,7 +17,9 @@ export class EditorPanel extends React.PureComponent {
startShotterMode = () => this.props.editor.changeMode(MODES.SHOTTER); startShotterMode = () => this.props.editor.changeMode(MODES.SHOTTER);
render() { render() {
const { mode, routerPoints } = this.props; const {
mode, routerPoints, editor, totalDistance, estimateTime
} = this.props;
return ( return (
<div> <div>
@ -23,6 +27,7 @@ export class EditorPanel extends React.PureComponent {
<EditorDialog <EditorDialog
mode={mode} mode={mode}
routerPoints={routerPoints} routerPoints={routerPoints}
editor={editor}
/> />
<div className="panel"> <div className="panel">
@ -36,6 +41,12 @@ export class EditorPanel extends React.PureComponent {
</div> </div>
<div className="panel right"> <div className="panel right">
<div className="control-dist">
{totalDistance} км
{
(estimateTime > 0) && (estimateTime > 0) && <span>{toHours(estimateTime)}</span>
}
</div>
<div className="control-bar"> <div className="control-bar">
<button <button
className={classnames({ active: mode === MODES.ROUTER })} className={classnames({ active: mode === MODES.ROUTER })}
@ -69,11 +80,18 @@ export class EditorPanel extends React.PureComponent {
</button> </button>
<button <button
className={classnames('highlighted', { active: mode === MODES.SHOTTER })} className={classnames({ active: mode === MODES.TRASH })}
onClick={this.startShotterMode}
>
<Icon icon="icon-trash" />
</button>
<button
className="highlighted"
onClick={this.startShotterMode} onClick={this.startShotterMode}
> >
<span>СХОРОНИТЬ</span> <span>СХОРОНИТЬ</span>
<Icon icon="icon-shooter" /> <Icon icon="icon-save" />
</button> </button>
</div> </div>
@ -81,4 +99,4 @@ export class EditorPanel extends React.PureComponent {
</div> </div>
); );
} }
}; }

View file

@ -1,54 +1,68 @@
import React from 'react'; import React from 'react';
const noPoints = () => ( const noPoints = ({ cancelDrawing }) => (
<div className="router-helper"> <div className="router-helper">
<div className="router-helper__text"> <div className="router-helper__text">
<div className="big white">Укажите на карте первую точку маршрута</div> <div className="big white">Укажите на карте первую точку маршрута</div>
<div className="small gray">Путь прокладывается по улицам, тротуарам и тропинкам</div> <div className="small gray">Путь прокладывается по улицам, тротуарам и тропинкам</div>
</div> </div>
<div className="router-helper__buttons"> <div className="router-helper__buttons">
<div className="button router-helper__button"> <div className="button router-helper__button" onClick={cancelDrawing}>
Отмена Отмена
</div> </div>
</div> </div>
</div> </div>
); );
const firstPoint = () => ( const firstPoint = ({ cancelDrawing }) => (
<div className="router-helper"> <div className="router-helper">
<div className="router-helper__text"> <div className="router-helper__text">
<div className="big white">Укажите на карте конечную точку маршрута</div> <div className="big white">Укажите на карте конечную точку маршрута</div>
<div className="small gray"> Вы сможете добавить уточняющие точки</div> <div className="small gray"> Вы сможете добавить уточняющие точки</div>
</div> </div>
<div className="router-helper__buttons"> <div className="router-helper__buttons">
<div className="button router-helper__button"> <div className="button router-helper__button" onClick={cancelDrawing}>
Отмена Отмена
</div> </div>
</div> </div>
</div> </div>
); );
const draggablePoints = () => ( const draggablePoints = ({ cancelDrawing, submitDrawing }) => (
<div className="router-helper"> <div className="router-helper">
<div className="router-helper__text"> <div className="router-helper__text">
<div className="big white">Продолжите маршрут, щелкая по карте</div> <div className="big white">Продолжите маршрут, щелкая по карте</div>
<div className="small gray">Потяните линию, чтобы указать промежуточные точки</div> <div className="small gray">Потяните линию, чтобы указать промежуточные точки</div>
</div> </div>
<div className="router-helper__buttons button-group"> <div className="router-helper__buttons button-group">
<div className="button button_red router-helper__button"> <div className="button button_red router-helper__button" onClick={cancelDrawing}>
Отмена Отмена
</div> </div>
<div className="button primary router-helper__button"> <div className="button primary router-helper__button" onClick={submitDrawing}>
Применить Применить
</div> </div>
</div> </div>
</div> </div>
); );
export const RouterHelper = ({ routerPoints }) => ( export class RouterHelper extends React.Component {
<div> cancelDrawing = () => {
{ !routerPoints && noPoints() } this.props.editor.router.cancelDrawing();
{ routerPoints === 1 && firstPoint() } };
{ routerPoints >= 2 && draggablePoints() }
</div> submitDrawing = () => {
); this.props.editor.router.submitDrawing();
};
render() {
const { routerPoints, editor } = this.props;
const { cancelDrawing, submitDrawing } = this;
return (
<div>
{!routerPoints && noPoints({ cancelDrawing })}
{routerPoints === 1 && firstPoint({ cancelDrawing })}
{routerPoints >= 2 && draggablePoints({ cancelDrawing, submitDrawing })}
</div>
);
}
}

View file

@ -3,5 +3,6 @@ export const MODES = {
STICKERS: 'STICKERS', STICKERS: 'STICKERS',
ROUTER: 'ROUTER', ROUTER: 'ROUTER',
SHOTTER: 'SHOTTER', SHOTTER: 'SHOTTER',
TRASH: 'TRASH',
NONE: 'NONE', NONE: 'NONE',
}; };

View file

@ -8,26 +8,36 @@ export class App extends React.Component {
state = { state = {
mode: 'none', mode: 'none',
routerPoints: 0, routerPoints: 0,
totalDistance: 0,
estimateTime: 0,
}; };
setMode = mode => { setMode = mode => {
this.setState({ mode }); this.setState({ mode });
}; };
setRouterPoints = routerPoints => { setRouterPoints = routerPoints => {
this.setState({ routerPoints }); this.setState({ routerPoints });
}; };
setTotalDist = totalDistance => {
const time = (totalDistance && (totalDistance / 15)) || 0;
const estimateTime = (time && parseFloat(time.toFixed(1)));
this.setState({ totalDistance, estimateTime });
};
editor = new Editor({ editor = new Editor({
container: 'map', container: 'map',
mode: this.state.mode, mode: this.state.mode,
setMode: this.setMode, setMode: this.setMode,
setRouterPoints: this.setRouterPoints, setRouterPoints: this.setRouterPoints,
setTotalDist: this.setTotalDist,
}); });
render() { render() {
const { const {
editor, editor,
state: { mode, routerPoints }, state: { mode, routerPoints, totalDistance, estimateTime },
} = this; } = this;
@ -38,6 +48,8 @@ export class App extends React.Component {
editor={editor} editor={editor}
mode={mode} mode={mode}
routerPoints={routerPoints} routerPoints={routerPoints}
totalDistance={totalDistance}
estimateTime={estimateTime}
/> />
</div> </div>
); );

View file

@ -11,14 +11,19 @@ export class Editor {
mode, mode,
setMode, setMode,
setRouterPoints, setRouterPoints,
setTotalDist,
}) { }) {
this.map = new Map({ container }); this.map = new Map({ container });
const { lockMapClicks, routerMoveStart, map: { map } } = this; const {
lockMapClicks, routerMoveStart, changeMode, pushPolyPoints, map: { map }
} = this;
this.poly = new Poly({ map, routerMoveStart, lockMapClicks }); this.poly = new Poly({ map, routerMoveStart, lockMapClicks, setTotalDist });
this.stickers = new Stickers({ map, lockMapClicks }); this.stickers = new Stickers({ map, lockMapClicks });
this.router = new Router({ map, lockMapClicks, setRouterPoints }); this.router = new Router({
map, lockMapClicks, setRouterPoints, changeMode, pushPolyPoints
});
this.shotter = new Shotter({ map }); this.shotter = new Shotter({ map });
this.setMode = setMode; this.setMode = setMode;
@ -98,6 +103,10 @@ export class Editor {
routerMoveStart = () => { routerMoveStart = () => {
const { _latlngs } = this.poly.poly; const { _latlngs } = this.poly.poly;
if (_latlngs) this.router.moveStart(_latlngs[_latlngs.length-1]); if (_latlngs) this.router.moveStart(_latlngs[_latlngs.length - 1]);
};
pushPolyPoints = latlngs => {
this.poly.pushPoints(latlngs);
} }
} }

View file

@ -1,16 +1,21 @@
import { polyline } from "leaflet"; import L from 'leaflet';
import 'leaflet-geometryutil';
import { simplify } from '$utils/simplify';
const polyStyle = { color: 'url(#activePathGradient)', weight: '6' }; const polyStyle = { color: 'url(#activePathGradient)', weight: '6' };
// const polyStyle = { color: '#ff3344', weight: '5' }; // const polyStyle = { color: '#ff3344', weight: '5' };
export class Poly { export class Poly {
constructor({ map, routerMoveStart, lockMapClicks }) { constructor({
this.poly = polyline([], polyStyle); map, routerMoveStart, lockMapClicks, setTotalDist
}) {
this.poly = L.polyline([], polyStyle);
this.latlngs = []; this.latlngs = [];
this.poly.addTo(map); this.poly.addTo(map);
this.map = map; this.map = map;
this.routerMoveStart = routerMoveStart; this.routerMoveStart = routerMoveStart;
this.setTotalDist = setTotalDist;
this.lockMapClicks = lockMapClicks; this.lockMapClicks = lockMapClicks;
this.bindEvents(); this.bindEvents();
} }
@ -19,7 +24,10 @@ export class Poly {
console.log('upd'); console.log('upd');
const coords = this.poly.toGeoJSON().geometry.coordinates; const coords = this.poly.toGeoJSON().geometry.coordinates;
this.latlngs = (coords && coords.length && coords.map(([lng, lat]) => ({ lng, lat }))) || []; this.latlngs = (coords && coords.length && coords.map(([lng, lat]) => ({ lng, lat }))) || [];
const meters = (this.poly && (L.GeometryUtil.length(this.poly) / 1000)) || 0;
const kilometers = (meters && parseFloat(meters.toFixed(1))) || 0;
this.setTotalDist(kilometers);
this.routerMoveStart(); this.routerMoveStart();
}; };
@ -59,9 +67,11 @@ export class Poly {
continue = () => { continue = () => {
if (this.latlngs && this.latlngs.length) { if (this.latlngs && this.latlngs.length) {
console.log('continue?');
this.poly.enableEdit().continueForward(); this.poly.enableEdit().continueForward();
this.poly.editor.reset(); this.poly.editor.reset();
} else { } else {
console.log('start over');
this.poly = this.map.editTools.startPolyline(); this.poly = this.map.editTools.startPolyline();
this.poly.setStyle(polyStyle); this.poly.setStyle(polyStyle);
} }
@ -78,5 +88,17 @@ export class Poly {
lockMap = () => { lockMap = () => {
this.lockMapClicks(true); this.lockMapClicks(true);
};
pushPoints = latlngs => {
const { map } = this;
const simplified = simplify({ map, latlngs });
const summary = [
...this.poly.getLatLngs(),
...simplified,
];
this.poly.setLatLngs(summary);
this.updateMarks();
} }
} }

View file

@ -2,9 +2,16 @@ import L from 'leaflet';
import Routing from 'leaflet-routing-machine/src/index'; import Routing from 'leaflet-routing-machine/src/index';
import { CONFIG } from '$config'; import { CONFIG } from '$config';
import { DomMarker } from '$utils/DomMarker'; import { DomMarker } from '$utils/DomMarker';
import { MODES } from '$constants/modes';
export class Router { export class Router {
constructor({ map, lockMapClicks, setRouterPoints }) { constructor({ map, lockMapClicks, setRouterPoints, changeMode, pushPolyPoints }) {
this.waypoints = [];
this.lockMapClicks = lockMapClicks;
this.setRouterPoints = setRouterPoints;
this.changeMode = changeMode;
this.pushPolyPoints = pushPolyPoints;
const routeLine = r => Routing.line(r, { const routeLine = r => Routing.line(r, {
styles: [ styles: [
{ color: 'white', opacity: 0.8, weight: 6 }, { color: 'white', opacity: 0.8, weight: 6 },
@ -36,9 +43,6 @@ export class Router {
this.router.addTo(map); this.router.addTo(map);
this.waypoints = [];
this.lockMapClicks = lockMapClicks;
this.setRouterPoints = setRouterPoints;
// this.router._line.on('mousedown', console.log); // this.router._line.on('mousedown', console.log);
} }
// //
@ -109,5 +113,20 @@ export class Router {
updateWaypointsCount = () => { updateWaypointsCount = () => {
const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng); const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
this.setRouterPoints(waypoints.length); this.setRouterPoints(waypoints.length);
} };
cancelDrawing = () => {
this.router.setWaypoints([]);
this.changeMode(MODES.NONE);
};
submitDrawing = () => {
const [route] = this.router._routes;
if (!route) return;
const { coordinates, summary: { totalDistance } } = route;
this.pushPolyPoints(coordinates);
this.router.setWaypoints([]);
this.changeMode(MODES.POLY);
};
} }

View file

@ -118,9 +118,9 @@ export class Sticker {
generateStickerSVG = sticker => ( generateStickerSVG = sticker => (
` `
<svg width="64" height="64"> <svg width="64" height="64">
<use xlink:href="${stickers}#sticker-${sticker}" x="0" y="0" width="64" height="64" /> <use xlink:href="${stickers}#sticker-${sticker}" x="0" y="0" width="64" height="64" />
</svg> </svg>
` `
) )
} }

View file

@ -32,11 +32,29 @@
</g> </g>
<g id="icon-shooter"> <g id="icon-shooter">
<rect x="0" y="0" width="32" height="32" fill="black" stroke="none" /> <rect x="0" y="0" width="32" height="32" fill="black" stroke="none" />
<g transform="translate(18 6)"> <g transform="translate(18 6)">
<path d="m0 0l-4.391.054c-1.418.531-2.34 1.756-3.176 3.102h-5.178c-.68.317-1.351.655-1.455 2.584v11.49c.17 1.001.58 1.765 1.455 2.06h22.537c.746-.044 1.288-.426 1.68-1.06v-13.517c-.185-1.643-.916-1.65-1.68-1.557h-6.62c-.326-1.26-1.91-2.247-3.172-3.156zm-2.122 5.289c3.227 0 5.87 2.626 5.87 5.846s-2.643 5.845-5.87 5.845c-3.227 0-5.869-2.626-5.869-5.845 0-3.22 2.642-5.846 5.87-5.846zm0 1.998a3.844 3.844 0 0 0-3.869 3.848 3.842 3.842 0 0 0 3.87 3.845 3.84 3.84 0 0 0 3.866-3.845 3.842 3.842 0 0 0-3.867-3.848z" fill="white" stroke="none" /> <path d="m0 0l-4.391.054c-1.418.531-2.34 1.756-3.176 3.102h-5.178c-.68.317-1.351.655-1.455 2.584v11.49c.17 1.001.58 1.765 1.455 2.06h22.537c.746-.044 1.288-.426 1.68-1.06v-13.517c-.185-1.643-.916-1.65-1.68-1.557h-6.62c-.326-1.26-1.91-2.247-3.172-3.156zm-2.122 5.289c3.227 0 5.87 2.626 5.87 5.846s-2.643 5.845-5.87 5.845c-3.227 0-5.869-2.626-5.869-5.845 0-3.22 2.642-5.846 5.87-5.846zm0 1.998a3.844 3.844 0 0 0-3.869 3.848 3.842 3.842 0 0 0 3.87 3.845 3.84 3.84 0 0 0 3.866-3.845 3.842 3.842 0 0 0-3.867-3.848z" fill="white" stroke="none" />
</g> </g>
</g> </g>
<g id="icon-trash">
<g transform="matrix(4.2 0 0 4.2 -3 -2 )" stroke="none">
<path fill="white" d="M2.783 3.626h2.923v2.923H2.783z"/>
<path fill="white" d="M2.479 2.597h3.508v.748H2.479z"/>
<path fill="white" d="M3.438 1.919h1.473v.865H3.438z"/>
<path fill="black" d="M3.859 2.25h.631v.386h-.631z"/>
<path fill="black" d="M3.134 3.906h.468v2.315h-.468z"/>
<path fill="black" d="M-4.537 3.906h.444v2.315h-.444z" transform="scale(-1 1)"/>
<path fill="black" d="M4.958 3.906h.444v2.315h-.444z"/>
</g>
</g>
<g id="icon-save" stroke="none">
<path fill="black" d="M0 0h32v32H0z"/>
<path fill="white" d="M6.844 8.459V24h18.312V11.375H14.031V8.459z"/>
<rect fill="black" width="2.74" height="5.038" x="14.63" y="14.411" stroke-width="3.603" />
<path fill="black" d="M16.866 19.73l-4.315-.06-4.314.06 2.209-3.707 2.105-3.766 2.106 3.766z" transform="matrix(.45903 -.40628 .79506 .23456 -3.467 21.088)"/>
</g>
</defs> </defs>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Before After
Before After

View file

@ -5,6 +5,15 @@
box-shadow: rgba(0,0,0,0.3) 0 2px 0, inset rgba(255, 255, 255, 0.05) 1px 1px; box-shadow: rgba(0,0,0,0.3) 0 2px 0, inset rgba(255, 255, 255, 0.05) 1px 1px;
} }
.control-dist {
height: 44px;
background: #222222;
padding: 0 10px;
display: flex;
align-items: center;
border-radius: 3px 0 0 3px;
}
.control-sep { .control-sep {
height: 44px; height: 44px;
background: #222222; background: #222222;

21
src/utils/simplify.js Normal file
View file

@ -0,0 +1,21 @@
import L from 'leaflet';
export const simplify = ({ map, latlngs }) => {
const points = [];
const target = [];
const zoom = 12;
const mul = 0.7; // 0 - not simplifying, 1 - very rude.
// its better to estimate mul value by route length
for (let i = 0; i < latlngs.length; i += 1) {
points.push(map.project({ lat: latlngs[i].lat, lng: latlngs[i].lng }, zoom));
}
const simplified = L.LineUtil.simplify(points, mul);
for (let i = 0; i < simplified.length; i += 1) {
target.push(map.unproject(simplified[i], zoom));
}
return target;
};

5
src/utils/time.js Normal file
View file

@ -0,0 +1,5 @@
export const toHours = (info) => {
const hrs = parseInt(Number(info));
const min = Math.round((Number(info)-hrs) * 60);
return hrs+':'+min;
}