From 99f76dbe2c0e32494bf3ff6ea570febf7a04b8cd Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 18 Feb 2019 12:02:06 +0700 Subject: [PATCH] gpx generation --- src/components/panels/UserPanel.tsx | 17 +++++++++++++-- src/redux/user/actions.ts | 1 + src/redux/user/constants.ts | 2 ++ src/redux/user/sagas.ts | 18 ++++++++++++++-- src/sprites/icon.svg | 5 +++++ src/utils/gpx.ts | 33 +++++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 src/utils/gpx.ts diff --git a/src/components/panels/UserPanel.tsx b/src/components/panels/UserPanel.tsx index a436b00..11bb0cb 100644 --- a/src/components/panels/UserPanel.tsx +++ b/src/components/panels/UserPanel.tsx @@ -4,7 +4,7 @@ import { GuestButton } from '$components/user/GuestButton'; import { DEFAULT_USER, IUser, ROLES } from '$constants/auth'; import { UserButton } from '$components/user/UserButton'; import { UserMenu } from '$components/user/UserMenu'; -import { setUser, userLogout, takeAShot, setDialog, gotVkUser, setDialogActive, openMapDialog } from '$redux/user/actions'; +import { setUser, userLogout, takeAShot, setDialog, gotVkUser, setDialogActive, openMapDialog, getGPXTrack } from '$redux/user/actions'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { Icon } from '$components/panels/Icon'; @@ -21,7 +21,8 @@ interface Props extends IRootState { gotVkUser: typeof gotVkUser, takeAShot: typeof takeAShot, openMapDialog: typeof openMapDialog, -}; + getGPXTrack: typeof getGPXTrack, +} interface State { menuOpened: boolean @@ -130,6 +131,17 @@ export class Component extends React.PureComponent { + +
+ +
+ +
); @@ -146,6 +158,7 @@ const mapDispatchToProps = dispatch => bindActionCreators({ gotVkUser, setDialogActive, openMapDialog, + getGPXTrack, }, dispatch); export const UserPanel = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/src/redux/user/actions.ts b/src/redux/user/actions.ts index 1081666..935914e 100644 --- a/src/redux/user/actions.ts +++ b/src/redux/user/actions.ts @@ -62,3 +62,4 @@ export const searchSetLoading = loading => ({ type: ACTIONS.SEARCH_SET_LOADING, export const searchPutRoutes = payload => ({ type: ACTIONS.SEARCH_PUT_ROUTES, ...payload }); export const setMarkersShown = markers_shown => ({ type: ACTIONS.SET_MARKERS_SHOWN, markers_shown }); +export const getGPXTrack = () => ({ type: ACTIONS.GET_GPX_TRACK }); diff --git a/src/redux/user/constants.ts b/src/redux/user/constants.ts index e0003e4..16122ea 100644 --- a/src/redux/user/constants.ts +++ b/src/redux/user/constants.ts @@ -68,4 +68,6 @@ export const ACTIONS: IActions = { SET_SPEED: 'SET_SPEED', SET_MARKERS_SHOWN: 'SET_MARKERS_SHOWN', + + GET_GPX_TRACK: 'GET_GPX_TRACK', }; diff --git a/src/redux/user/sagas.ts b/src/redux/user/sagas.ts index 8f38021..88dcfaf 100644 --- a/src/redux/user/sagas.ts +++ b/src/redux/user/sagas.ts @@ -22,7 +22,7 @@ import { import { getUrlData, parseQuery, pushLoaderState, pushNetworkInitError, pushPath, replacePath } from '$utils/history'; import { editor } from '$modules/Editor'; import { ACTIONS } from '$redux/user/constants'; -import { IModes, MODES } from '$constants/modes'; +import { MODES } from '$constants/modes'; import { DEFAULT_USER } from '$constants/auth'; import { TIPS } from '$constants/tips'; import { @@ -34,11 +34,12 @@ import { imageFetcher } from '$utils/renderer'; import { ILogos, LOGOS } from '$constants/logos'; -import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers'; +import { DEFAULT_PROVIDER } from '$constants/providers'; import { DIALOGS } from '$constants/dialogs'; import * as ActionCreators from '$redux/user/actions'; import { IRootState } from "$redux/user/reducer"; +import { downloadGPXTrack, getGPXString } from "$utils/gpx"; const getUser = state => (state.user.user); const getState = state => (state.user); @@ -558,6 +559,17 @@ function* setTitleSaga({ title }: ReturnType):Sa if (title) { document.title = `${title} | Редактор маршрутов`; } } +function* getGPXTrackSaga(): SagaIterator { + const { route } = editor.dumpData(); + const { title, address } = yield select(getState); + + if (!route || route.length <= 0) return; + + const track = getGPXString({ points: route, title: (title || address) }); + + return downloadGPXTrack({ track, title }); +} + export function* userSaga() { yield takeLatest(REHYDRATE, authCheckSaga); yield takeEvery(ACTIONS.SET_MODE, setModeSaga); @@ -601,4 +613,6 @@ export function* userSaga() { yield takeLatest(ACTIONS.OPEN_MAP_DIALOG, openMapDialogSaga); yield takeLatest(ACTIONS.SEARCH_SET_TAB, searchSetTabSaga); yield takeLatest(ACTIONS.SET_USER, setUserSaga); + + yield takeLatest(ACTIONS.GET_GPX_TRACK, getGPXTrackSaga) } diff --git a/src/sprites/icon.svg b/src/sprites/icon.svg index c1567be..d8be6ff 100644 --- a/src/sprites/icon.svg +++ b/src/sprites/icon.svg @@ -369,6 +369,11 @@ + + + + + diff --git a/src/utils/gpx.ts b/src/utils/gpx.ts new file mode 100644 index 0000000..d8c431c --- /dev/null +++ b/src/utils/gpx.ts @@ -0,0 +1,33 @@ +import * as saveAs from 'file-saver'; + +export interface IRoutePoint { + lat: number, + lng: number, +} + +interface IGetGPXString { + points: Array, + title?: string, +} + +export const getGPXString = ({ points, title }: IGetGPXString): string => (` + + + + ${title || 'GPX Track'} + ${ + points.reduce((cat, { lat, lng }, index) => ( + `${cat} +` + ), '') + } + + +`); + +export const downloadGPXTrack = ({ track, title }: { track: string, title?: string }) => ( + saveAs( + new Blob([track], { type: 'application/gpx+xml;charset=utf-8' }), + `${title || 'track'}.gpx` + ) +);