diff --git a/src/components/panels/EditorPanel.jsx b/src/components/panels/EditorPanel.jsx index 5eed69c..e486ac5 100644 --- a/src/components/panels/EditorPanel.jsx +++ b/src/components/panels/EditorPanel.jsx @@ -103,6 +103,13 @@ class Component extends React.PureComponent { + + - -
@@ -37,3 +32,11 @@ export const RendererPanel = ({ onCancel, onSubmit }): Props => (
); + +/* +
+ +
+ */ diff --git a/src/config.js b/src/config.js index 623f636..1dba195 100644 --- a/src/config.js +++ b/src/config.js @@ -1,4 +1,4 @@ -import { providers } from '$constants/providers'; +import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers'; export const CONFIG = { OSRM_URL: 'http://vault48.org:5000/route/v1', @@ -9,4 +9,4 @@ export const COLORS = { PATH_COLOR: ['#ff7700', '#ff3344'], }; -export const PROVIDER = providers.blank; +export const PROVIDER = PROVIDERS[DEFAULT_PROVIDER]; diff --git a/src/constants/modes.js b/src/constants/modes.js index 5813132..32c0ee4 100644 --- a/src/constants/modes.js +++ b/src/constants/modes.js @@ -7,5 +7,6 @@ export const MODES = { NONE: 'NONE', LOGO: 'LOGO', SAVE: 'SAVE', - CONFIRM_CANCEL: 'CONFIRM_CANCEL' + CONFIRM_CANCEL: 'CONFIRM_CANCEL', + PROVIDER: 'PROVIDER', }; diff --git a/src/constants/providers.js b/src/constants/providers.js index 0586d57..c5b5927 100644 --- a/src/constants/providers.js +++ b/src/constants/providers.js @@ -1,12 +1,56 @@ // Стили карт -export const providers = { - 'watercolor': 'http://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg', - 'darq': 'http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', - 'dgis': 'https://tile1.maps.2gis.com/tiles?x={x}&y={y}&z={z}&v=1', - 'default': 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', - 'hot': 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', - 'blank': 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', - 'sat': 'http://mt0.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', - 'ymap': 'https://vec03.maps.yandex.net/tiles?l=map&v=17.04.16-0&x={x}&y={y}&z={z}&scale=1&lang=ru_RU', - 'ysat': 'https://sat02.maps.yandex.net/tiles?l=sat&v=3.330.0&x={x}&y={y}&z={z}&lang=ru_RU' +const TILEMAPS = { + WATERCOLOR: { + name: 'Watercolor', + url: 'http://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg', + range: [1, 4], + }, + DGIS: { + name: '2gis', + url: 'https://tile1.maps.2gis.com/tiles?x={x}&y={y}&z={z}&v=1', + range: [1, 3], + }, + DEFAULT: { + name: 'OpenStreetMap', + url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + range: [1, 4], + }, + DARQ: { + name: 'Darq', + url: 'http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', + range: [1, 4], + }, + BLANK: { + name: 'Blanque', + url: 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', + range: [1, 4], + }, + HOT: { + name: 'Hot', + url: 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', + range: [1, 4], + }, + SAY: { + name: 'Google?', + url: 'http://mt{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', + range: [0, 3], + }, + YMAP: { + name: 'Yandex', + url: 'https://vec0{s}.maps.yandex.net/tiles?l=map&v=17.04.16-0&x={x}&y={y}&z={z}&scale=1&lang=ru_RU', + range: [1, 4], + }, + YSAT: { + name: 'YandexSat', + url: 'https://sat0{s}.maps.yandex.net/tiles?l=sat&v=3.330.0&x={x}&y={y}&z={z}&lang=ru_RU', + range: [1, 4], + }, }; + +const ENABLED = ['BLANK', 'DEFAULT', 'DGIS']; + +export const DEFAULT_PROVIDER = ENABLED[0]; +export const PROVIDERS = ENABLED.reduce((obj, provider) => ({ + ...obj, + [provider]: TILEMAPS[provider], +}), {}); diff --git a/src/containers/App.jsx b/src/containers/App.jsx index e396fd6..6d744bb 100644 --- a/src/containers/App.jsx +++ b/src/containers/App.jsx @@ -23,10 +23,7 @@ const Component = (props: Props) => ( - { - props.renderer_active && - - } + { props.renderer_active && } ); diff --git a/src/modules/Editor.js b/src/modules/Editor.js index 31a0029..f47ad06 100644 --- a/src/modules/Editor.js +++ b/src/modules/Editor.js @@ -18,6 +18,7 @@ import { setRouterPoints, setTitle, } from '$redux/user/actions'; +import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers'; export class Editor { constructor() { @@ -27,6 +28,7 @@ export class Editor { this.initialData = {}; this.activeSticker = null; this.mode = MODES.NONE; + this.provider = PROVIDERS[DEFAULT_PROVIDER]; const { triggerOnChange, lockMapClicks, routerMoveStart, changeMode, pushPolyPoints, @@ -241,7 +243,7 @@ export class Editor { }; fitDrawing = () => { - if (this.poly.isEmpty()) return; + if (this.poly.isEmpty) return; const bounds = this.poly.poly.getBounds(); if (bounds && Object.values(bounds)) this.map.map.fitBounds(bounds); @@ -289,7 +291,7 @@ export class Editor { }; cancelEditing = () => { - if (this.hasEmptyHistory()) { + if (this.hasEmptyHistory) { this.clearAll(); this.startEditing(); } else { @@ -307,19 +309,18 @@ export class Editor { stickers: this.stickers.dumpData(), }); - // isEmpty = () => { - // const { route, stickers } = this.dumpData(); - // - // return (route.length > 1 && stickers.length > 0); - // }; + setProvider = provider => { + this.provider = provider; + this.map.setProvider(provider); + }; - isEmpty = () => { + get isEmpty() { const { route, stickers } = this.dumpData(); return (!route || route.length < 1) && (!stickers || stickers.length <= 0); - }; + } - hasEmptyHistory = () => { + get hasEmptyHistory() { const { route, stickers } = this.initialData; return (!route || route.length < 1) && (!stickers || stickers.length <= 0); diff --git a/src/modules/Map.js b/src/modules/Map.js index 617d9f5..fd2f460 100644 --- a/src/modules/Map.js +++ b/src/modules/Map.js @@ -1,16 +1,14 @@ import { map, tileLayer } from 'leaflet'; -// import { Map as map } from 'leaflet/src/map/Map'; -// import { TileLayer as tileLayer } from 'leaflet/src/layer/tile/TileLayer'; - import 'leaflet/dist/leaflet.css'; import 'leaflet-editable'; import { PROVIDER } from '$config'; +import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers'; export class Map { constructor({ container }) { this.map = map(container, { editable: true }).setView([55.0153275, 82.9071235], 13); - this.tileLayer = tileLayer(PROVIDER, { + this.tileLayer = tileLayer(PROVIDER.url, { attribution: 'Независимое Велосообщество', maxNativeZoom: 18, maxZoom: 18, @@ -18,4 +16,10 @@ export class Map { this.tileLayer.addTo(this.map); } + + setProvider = provider => { + const { url } = (provider && PROVIDERS[provider] && PROVIDERS[provider]) || PROVIDERS[DEFAULT_PROVIDER]; + + this.tileLayer.setUrl(url); + } } diff --git a/src/modules/Poly.js b/src/modules/Poly.js index 21ee1bd..8bb12d6 100644 --- a/src/modules/Poly.js +++ b/src/modules/Poly.js @@ -161,5 +161,7 @@ export class Poly { dumpData = () => this.latlngs; - isEmpty = () => !this.latlngs || Object.values(this.latlngs).length < 0; + get isEmpty() { + return (!this.latlngs || Object.values(this.latlngs).length <= 0); + } } diff --git a/src/redux/store.js b/src/redux/store.js index 11f93dc..1cbbbb4 100644 --- a/src/redux/store.js +++ b/src/redux/store.js @@ -9,7 +9,7 @@ import { userSaga } from '$redux/user/sagas'; const userPersistConfig = { key: 'user', - whitelist: ['user', 'logo'], + whitelist: ['user', 'logo', 'provider'], storage, }; diff --git a/src/redux/user/actions.js b/src/redux/user/actions.js index 7d5df87..d244384 100644 --- a/src/redux/user/actions.js +++ b/src/redux/user/actions.js @@ -38,3 +38,4 @@ export const setRenderer = payload => ({ type: ACTIONS.SET_RENDERER, payload }); export const takeAShot = () => ({ type: ACTIONS.TAKE_A_SHOT }); export const cropAShot = payload => ({ type: ACTIONS.CROP_A_SHOT, ...payload }); +export const setProvider = provider => ({ type: ACTIONS.SET_PROVIDER, provider }); diff --git a/src/redux/user/constants.js b/src/redux/user/constants.js index 26b4304..b66cb41 100644 --- a/src/redux/user/constants.js +++ b/src/redux/user/constants.js @@ -36,4 +36,6 @@ export const ACTIONS = { SET_RENDERER: 'SET_RENDERER', TAKE_A_SHOT: 'TAKE_A_SHOT', CROP_A_SHOT: 'CROP_A_SHOT', + + SET_PROVIDER: 'SET_PROVIDER', }; diff --git a/src/redux/user/reducer.js b/src/redux/user/reducer.js index 65e44ae..dc70f6b 100644 --- a/src/redux/user/reducer.js +++ b/src/redux/user/reducer.js @@ -4,6 +4,7 @@ import { DEFAULT_USER } from '$constants/auth'; import { MODES } from '$constants/modes'; import { DEFAULT_LOGO } from '$constants/logos'; import { TIPS } from '$constants/tips'; +import { DEFAULT_PROVIDER } from '$constants/providers'; const getEstimated = distance => { const time = (distance && (distance / 15)) || 0; @@ -70,6 +71,8 @@ const setRenderer = (state, { payload }) => ({ renderer: { ...state.renderer, ...payload } }); +const setProvider = (state, { provider }) => ({ ...state, provider }); + const HANDLERS = { [ACTIONS.SET_USER]: setUser, [ACTIONS.SET_EDITING]: setEditing, @@ -91,6 +94,8 @@ const HANDLERS = { [ACTIONS.SHOW_RENDERER]: showRenderer, [ACTIONS.HIDE_RENDERER]: hideRenderer, [ACTIONS.SET_RENDERER]: setRenderer, + + [ACTIONS.SET_PROVIDER]: setProvider, }; export const INITIAL_STATE = { @@ -105,6 +110,7 @@ export const INITIAL_STATE = { title: '', address: '', changed: false, + provider: DEFAULT_PROVIDER, save_error: '', save_finished: false, diff --git a/src/redux/user/sagas.js b/src/redux/user/sagas.js index 3587c1e..7805726 100644 --- a/src/redux/user/sagas.js +++ b/src/redux/user/sagas.js @@ -27,6 +27,7 @@ import { imageFetcher } from '$utils/renderer'; import { LOGOS } from '$constants/logos'; +import { DEFAULT_PROVIDER } from '$constants/providers'; const getUser = state => (state.user.user); const getState = state => (state.user); @@ -46,11 +47,12 @@ function* generateGuestSaga() { } function* startEmptyEditorSaga() { - const { id, random_url } = yield select(getUser); + const { id, random_url, provider = DEFAULT_PROVIDER } = yield select(getUser); pushPath(`/${random_url}/edit`); editor.owner = id; + editor.setProvider(provider); editor.startEditing(); yield put(setChanged(false)); @@ -78,7 +80,7 @@ function* stopEditingSaga() { yield put(setChanged(false)); - yield put(setEditing(editor.hasEmptyHistory())); // don't close editor if no previous map + yield put(setEditing(editor.hasEmptyHistory)); // don't close editor if no previous map } function* mapInitSaga() { @@ -193,9 +195,7 @@ function* clearSaga({ type }) { } function* sendSaveRequestSaga({ title, address, force }) { - if (editor.isEmpty()) { - return yield put(setSaveError(TIPS.SAVE_EMPTY)); - } + if (editor.isEmpty) return yield put(setSaveError(TIPS.SAVE_EMPTY)); const { route, stickers } = editor.dumpData(); const { id, token } = yield select(getUser); @@ -239,6 +239,7 @@ function* getRenderData() { const images = yield fetchImages(ctx, geometry); yield composeImages({ geometry, images, ctx }); + yield composePoly({ points, ctx }); return yield canvas.toDataURL('image/jpeg'); @@ -254,7 +255,6 @@ function* takeAShotSaga() { return true; } - function* getCropData({ x, y, width, height }) { @@ -285,6 +285,10 @@ function* cropAShotSaga(params) { return yield put(hideRenderer()); } +function setProviderSaga({ provider }) { + editor.setProvider(provider); +} + export function* userSaga() { // ASYNCHRONOUS!!! :-) @@ -311,4 +315,6 @@ export function* userSaga() { yield takeLatest(ACTIONS.SET_SAVE_SUCCESS, setSaveSuccessSaga); yield takeLatest(ACTIONS.TAKE_A_SHOT, takeAShotSaga); yield takeLatest(ACTIONS.CROP_A_SHOT, cropAShotSaga); + + yield takeEvery(ACTIONS.SET_PROVIDER, setProviderSaga); } diff --git a/src/styles/panel.less b/src/styles/panel.less index 9c14125..8d9bd33 100644 --- a/src/styles/panel.less +++ b/src/styles/panel.less @@ -135,7 +135,7 @@ } &.single { - border-radius: 3px; + border-radius: @panel_radius; } svg { diff --git a/src/utils/renderer.js b/src/utils/renderer.js index a3d96e6..8870335 100644 --- a/src/utils/renderer.js +++ b/src/utils/renderer.js @@ -1,6 +1,7 @@ import { editor } from '$modules/Editor'; import { COLORS, CONFIG } from '$config'; import saveAs from 'file-saver'; +import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers'; const latLngToTile = latlng => { const { map } = editor.map; @@ -64,7 +65,14 @@ export const getPolyPlacement = () => ( : editor.poly.poly.getLatLngs().map((latlng) => ({ ...editor.map.map.latLngToContainerPoint(latlng) })) ); -const getImageSource = ({ x, y, zoom }) => (`http://b.basemaps.cartocdn.com/light_all/${zoom}/${x}/${y}.png`); +const replaceProviderUrl = (provider, { x, y, zoom }) => { + const { url, range } = (PROVIDERS[editor.provider] || PROVIDERS[DEFAULT_PROVIDER]); + const random = (range && range.length >= 2) ? Math.round((Math.random() * (range[1] - range[0])) + range[0]) : 1; + + return url.replace('{x}', x).replace('{y}', y).replace('{z}', zoom).replace('{s}', random); +}; + +const getImageSource = coords => replaceProviderUrl(editor.provider, coords); export const imageFetcher = source => new Promise((resolve, reject) => { const img = new Image(); @@ -108,6 +116,8 @@ export const composeImages = ({ images, geometry, ctx }) => { }; export const composePoly = ({ points, ctx }) => { + if (editor.poly.isEmpty) return; + let minX = points[0].x; let maxX = points[0].x; let minY = points[0].y;