replacing gpx tracks

This commit is contained in:
Fedor Katurov 2020-02-11 11:04:57 +07:00
parent acca2aba14
commit 1d45e65434
5 changed files with 86 additions and 56 deletions

View file

@ -1,9 +1,9 @@
import React, { FC, useCallback, ChangeEvent } from 'react'; import React, { FC, useCallback, ChangeEvent } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import * as EDITOR_ACTIONS from '~/redux/editor/actions';
import { IState } from '~/redux/store'; import { IState } from '~/redux/store';
import { selectEditorGpx } from '~/redux/editor/selectors'; import { selectEditorGpx } from '~/redux/editor/selectors';
import { GpxDialogRow } from '~/components/gpx/GpxDialogRow'; import { GpxDialogRow } from '~/components/gpx/GpxDialogRow';
import { GpxConfirm } from '~/components/gpx/GpxConfirm';
import { MainMap } from '~/constants/map'; import { MainMap } from '~/constants/map';
import { latLngBounds } from 'leaflet'; import { latLngBounds } from 'leaflet';
import { Switch } from '../Switch'; import { Switch } from '../Switch';
@ -13,6 +13,10 @@ import uuid from 'uuid';
import { getUrlData } from '~/utils/history'; import { getUrlData } from '~/utils/history';
import { getRandomColor } from '~/utils/dom'; import { getRandomColor } from '~/utils/dom';
import * as EDITOR_ACTIONS from '~/redux/editor/actions';
import * as MAP_ACTIONS from '~/redux/map/actions';
import { simplify } from '~/utils/simplify';
const mapStateToProps = (state: IState) => ({ const mapStateToProps = (state: IState) => ({
gpx: selectEditorGpx(state), gpx: selectEditorGpx(state),
route: selectMapRoute(state), route: selectMapRoute(state),
@ -25,6 +29,7 @@ const mapDispatchToProps = {
editorUploadGpx: EDITOR_ACTIONS.editorUploadGpx, editorUploadGpx: EDITOR_ACTIONS.editorUploadGpx,
editorSetGpx: EDITOR_ACTIONS.editorSetGpx, editorSetGpx: EDITOR_ACTIONS.editorSetGpx,
editorGetGPXTrack: EDITOR_ACTIONS.editorGetGPXTrack, editorGetGPXTrack: EDITOR_ACTIONS.editorGetGPXTrack,
mapSetRoute: MAP_ACTIONS.mapSetRoute,
}; };
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {}; type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
@ -37,7 +42,12 @@ const GpxDialogUnconnected: FC<Props> = ({
editorGetGPXTrack, editorGetGPXTrack,
editorSetGpx, editorSetGpx,
editorUploadGpx, editorUploadGpx,
mapSetRoute,
}) => { }) => {
const toggleGpx = useCallback(() => {
editorSetGpx({ enabled: !gpx.enabled });
}, [gpx, editorSetGpx]);
const onGpxUpload = useCallback( const onGpxUpload = useCallback(
(event: ChangeEvent<HTMLInputElement>) => { (event: ChangeEvent<HTMLInputElement>) => {
if (!event.target || !event.target.files || event.target.files.length === 0) { if (!event.target || !event.target.files || event.target.files.length === 0) {
@ -76,13 +86,10 @@ const GpxDialogUnconnected: FC<Props> = ({
[gpx, editorSetGpx] [gpx, editorSetGpx]
); );
const toggleGpx = useCallback(() => {
editorSetGpx({ enabled: !gpx.enabled });
}, [gpx, editorSetGpx]);
const onRouteToggle = useCallback( const onRouteToggle = useCallback(
index => { index => {
if (!gpx.enabled) return; if (!gpx.enabled) return;
editorSetGpx({ editorSetGpx({
list: gpx.list.map((el, i) => (i !== index ? el : { ...el, enabled: !el.enabled })), list: gpx.list.map((el, i) => (i !== index ? el : { ...el, enabled: !el.enabled })),
}); });
@ -109,22 +116,19 @@ const GpxDialogUnconnected: FC<Props> = ({
}); });
}, [route, gpx, editorSetGpx]); }, [route, gpx, editorSetGpx]);
const onRouteReplace = useCallback(
(i: number) => {
mapSetRoute(simplify(gpx.list[i].latlngs));
editorSetGpx({
list: gpx.list.map((el, index) => (i !== index ? el : { ...el, enabled: false })),
});
},
[gpx, mapSetRoute, editorSetGpx]
);
return ( return (
<div className="control-dialog control-dialog__left control-dialog__small"> <div className="control-dialog control-dialog__left control-dialog__small">
{false && (
<div className="gpx-confirm">
<div className="gpx-confirm__text">Маршрут уже нанесен. Что делаем?</div>
<div className="gpx-confirm__buttons">
<div className="button success">Соединить</div>
<div className="button danger">Переписать</div>
<div className="button primary">Отмена</div>
</div>
</div>
)}
<div className="gpx-title"> <div className="gpx-title">
<div className="flex_1 big white upper">Треки</div> <div className="flex_1 big white upper">Треки</div>
<Switch active={gpx.enabled} onPress={toggleGpx} /> <Switch active={gpx.enabled} onPress={toggleGpx} />
@ -140,6 +144,7 @@ const GpxDialogUnconnected: FC<Props> = ({
onFocusRoute={onFocusRoute} onFocusRoute={onFocusRoute}
onRouteToggle={onRouteToggle} onRouteToggle={onRouteToggle}
onRouteColor={onRouteColor} onRouteColor={onRouteColor}
onRouteReplace={onRouteReplace}
/> />
))} ))}

View file

@ -0,0 +1,21 @@
import React, { FC } from 'react';
interface IProps {}
const GpxConfirm: FC<IProps> = ({}) => {
return (
<div className="gpx-confirm">
<div className="gpx-confirm__text">Маршрут уже нанесен. Что делаем?</div>
<div className="gpx-confirm__buttons">
<div className="button success">Соединить</div>
<div className="button danger">Переписать</div>
<div className="button primary">Отмена</div>
</div>
</div>
);
};
export { GpxConfirm };

View file

@ -1,4 +1,4 @@
import React, { FC } from 'react'; import React, { FC, memo } from 'react';
import { IGpxRoute } from '~/redux/editor'; import { IGpxRoute } from '~/redux/editor';
import { Switch } from '../Switch'; import { Switch } from '../Switch';
import { Icon } from '../panels/Icon'; import { Icon } from '../panels/Icon';
@ -13,44 +13,48 @@ interface IProps {
onRouteDrop: (i: number) => void; onRouteDrop: (i: number) => void;
onRouteToggle: (i: number) => void; onRouteToggle: (i: number) => void;
onRouteColor: (i: number) => void; onRouteColor: (i: number) => void;
onRouteReplace: (i: number) => void;
} }
const GpxDialogRow: FC<IProps> = ({ const GpxDialogRow: FC<IProps> = memo(
item, ({
index, item,
enabled, index,
onRouteToggle, enabled,
onFocusRoute, onRouteToggle,
onRouteDrop, onFocusRoute,
onRouteColor, onRouteDrop,
}) => { onRouteColor,
return ( onRouteReplace,
<div className={classnames('gpx-row', { 'gpx-row_disabled': !enabled || !item.enabled })}> }) => {
<div return (
className="gpx-row__color" <div className={classnames('gpx-row', { 'gpx-row_disabled': !enabled || !item.enabled })}>
style={{ backgroundColor: item.color }} <div
onClick={() => onRouteColor(index)} className="gpx-row__color"
/> style={{ backgroundColor: item.color }}
onClick={() => onRouteColor(index)}
/>
<div className="gpx-row__title" onClick={() => onFocusRoute(index)}> <div className="gpx-row__title" onClick={() => onFocusRoute(index)}>
{item.name} {item.name}
</div> </div>
<div className="gpx-row__buttons"> <div className="gpx-row__buttons">
{false && ( <div onClick={() => onRouteReplace(index)}>
<div onClick={() => onRouteDrop(index)}>
<Icon icon="icon-to-poly" size={24} /> <Icon icon="icon-to-poly" size={24} />
</div> </div>
)}
<div onClick={() => onRouteDrop(index)}> <div onClick={() => onRouteDrop(index)}>
<Icon icon="icon-trash-6" size={24} /> <Icon icon="icon-trash-6" size={24} />
</div> </div>
<div>
<Switch active={item.enabled} onPress={() => onRouteToggle(index)} /> <div>
<Switch active={item.enabled} onPress={() => onRouteToggle(index)} />
</div>
</div> </div>
</div> </div>
</div> );
); }
}; );
export { GpxDialogRow }; export { GpxDialogRow };

View file

@ -294,7 +294,7 @@ function* routerSubmit() {
const route: ReturnType<typeof selectMapRoute> = yield select(selectMapRoute); const route: ReturnType<typeof selectMapRoute> = yield select(selectMapRoute);
const latlngs: LatLng[] = path(['_routes', 0, 'coordinates'], OsrmRouter); const latlngs: LatLng[] = path(['_routes', 0, 'coordinates'], OsrmRouter);
const coordinates = simplify({ map: MainMap, latlngs }); const coordinates = simplify(latlngs);
yield put(mapSetRoute([...route, ...coordinates])); yield put(mapSetRoute([...route, ...coordinates]));
OsrmRouter.setWaypoints([]); OsrmRouter.setWaypoints([]);

View file

@ -1,9 +1,9 @@
import { Map, LineUtil, LatLng } from 'leaflet'; import { Map, LineUtil, LatLng } from 'leaflet';
// import { ILatLng } from "~/redux/map/types"; import { MainMap } from '~/constants/map';
export const simplify = ({ map, latlngs }: { map: Map, latlngs: LatLng[] }): LatLng[] => { export const simplify = (latlngs: LatLng[]): LatLng[] => {
const zoom = 12; const zoom = 12;
const mul = 0.7; // 0 - not simplifying, 1 - very rude. const mul = 0.7; // 0 - not simplifying, 1 - very rude.
const points = latlngs.map(({ lat, lng }) => map.project({ lat, lng }, zoom)); const points = latlngs.map(({ lat, lng }) => MainMap.project({ lat, lng }, zoom));
return LineUtil.simplify(points, mul).map(item => map.unproject(item, zoom)); return LineUtil.simplify(points, mul).map(item => MainMap.unproject(item, zoom));
}; };