diff --git a/package-lock.json b/package-lock.json
index 1f57e5c..241d7ff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7413,6 +7413,11 @@
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.3.3.tgz",
"integrity": "sha512-R9Cu5s0bdEXb9zh0nU17pV00IEvRh4xpWR9g1Oqz17jEDuMtkhy6DoYN1Q5WjvoDMRmq389zDVueUs9A2uWZSg=="
},
+ "leaflet-editable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/leaflet-editable/-/leaflet-editable-1.1.0.tgz",
+ "integrity": "sha1-93dZekCoGic/KHtIn9D+XM1gyNA="
+ },
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
diff --git a/package.json b/package.json
index bad5f57..d57b7dd 100644
--- a/package.json
+++ b/package.json
@@ -47,6 +47,7 @@
"clean-webpack-plugin": "^0.1.9",
"history": "^4.7.2",
"leaflet": "^1.3.3",
+ "leaflet-editable": "^1.1.0",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-hot-loader": "^4.1.1",
diff --git a/src/constants/modes.js b/src/constants/modes.js
new file mode 100644
index 0000000..efa6a0c
--- /dev/null
+++ b/src/constants/modes.js
@@ -0,0 +1,5 @@
+export const MODES = {
+ POLY: 'POLY',
+ STICKERS: 'STICKERS',
+ NONE: 'NONE',
+};
diff --git a/src/constants/providers.js b/src/constants/providers.js
new file mode 100644
index 0000000..7035606
--- /dev/null
+++ b/src/constants/providers.js
@@ -0,0 +1,12 @@
+// Стили карт
+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',
+ '2gis': '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'
+};
diff --git a/src/constants/stickers.js b/src/constants/stickers.js
new file mode 100644
index 0000000..385d040
--- /dev/null
+++ b/src/constants/stickers.js
@@ -0,0 +1,43 @@
+// Стикеры
+import L from "leaflet";
+
+export const stickers = {
+ 'objects': {},
+ 'layers': L.layerGroup(),
+ 'savedata': {},
+ 'layer_to_object': {},
+ 'src': [
+ {off: 5, title: 'Александр 3', title_long: 'Парк Городское Начало', latlng: [55.01275, 82.92368]},
+ {off: 9, title: 'пл.Калинина', title_long: "пл.Калинина", latlng: [55.06019, 82.91316]},
+ {off: 4, title: 'Мост', title_long: 'Мост', latlng: [55.00511, 82.93073]},
+ {off: 7, title: 'Икея', title_long: "Парковка ТЦ Мега", latlng: [54.96494, 82.93138]},
+ {off: 8, title: 'Бугринка', title_long: "Та самая коса\n(культовое место Усталых Педалек)", latlng: [54.97626, 82.95703]},
+ {off: 10, title: 'ГПНТБ', title_long: "ГПНТБ", latlng: [55.01665, 82.94629]}, // второй ряд
+ {off: 18, title: 'Оперный', title_long: "Оперный театр", latlng: [55.03027, 82.92292]},
+ {off: 1, title: 'Лес', title_long: 'Берёзовая роща', latlng: [55.04572, 82.95]}, // первый ряд
+ {off: 19, title: 'Пусто', title_long: "Пока что пусто 1"},
+ {off: 20, title: 'Пусто', title_long: "Пока что пусто 2"}, // третий ряд
+
+ {off: 2, title: 'Трасса', title_long: 'Дорога'},
+ {off: 3, title: 'Курочка', title_long: 'Курочка'},
+ {off: 6, title: 'Палатка', title_long: 'Палаточный лагерь'},
+ {off: 11, title: 'Фастфуд', title_long: "Двухколёсное ожирение"},
+ {off: 12, title: 'Пивко', title_long: "В Питере - пить!"},
+ {off: 13, title: 'Шаварма', title_long: "Вкусная шаурма"},
+ {off: 14, title: 'Камни', title_long: "Кааааммммуушшшки"},
+ {off: 15, title: 'Болото', title_long: "Пошла ты,\nтрясина грёбаная!"},
+ {off: 16, title: 'Роджер', title_long: "Может не надо?"},
+ {off: 17, title: 'Какашка', title_long: "Нехорошее место"},
+
+ {off: 21, title: 'Старт', title_long: "Старт здесь"},
+ {off: 22, title: '1', title_long: "Первая точка"},
+ {off: 23, title: '2', title_long: "Вторая точка"},
+ {off: 24, title: '3', title_long: "Третья точка"},
+ {off: 25, title: '4', title_long: "Четвёртая точка"},
+ {off: 26, title: '5', title_long: "Пятая точка"},
+ {off: 27, title: '7', title_long: "Шестая точка"},
+ {off: 28, title: 'Финиш', title_long: "Финиш здесь"},
+ {off: 29, title: 'Осторожно!', title_long: "Осторожно!"},
+ {off: 30, title: 'Вопрос', title_long: "Что тут?"}
+ ]
+};
diff --git a/src/containers/App.jsx b/src/containers/App.jsx
index a4a55d8..0defa4e 100644
--- a/src/containers/App.jsx
+++ b/src/containers/App.jsx
@@ -1,16 +1,50 @@
import React from 'react';
-import { Map } from '$modules/map';
-import { MapScreen } from "$styles/mapScreen";
+import { Editor } from '$modules/Editor';
+
+import { MapScreen } from '$styles/mapScreen';
+import { ControlsScreen } from '$styles/controlsScreen';
+import { MODES } from '$constants/modes';
export class App extends React.Component {
+ state = {
+ mode: 'none',
+ };
+
componentDidMount() {
- this.map = new Map('map');
+ const container = 'map';
+ const { mode } = this.state;
+
+ this.editor = new Editor({
+ container,
+ mode,
+ setMode: this.setMode,
+ });
}
+ setMode = mode => {
+ this.setState({ mode });
+ };
+
+ startPolyMode = () => this.editor.changeMode(MODES.POLY);
+
+ startStickerMode = () => this.editor.changeMode(MODES.STICKERS);
+
render() {
+ const { mode } = this.state;
+
return (
-
+
+
+
+
+
+
+
);
}
-};
+}
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..444bdef
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ @MAP
+
+
+
+
+
+
+
+
diff --git a/src/modules/Editor.js b/src/modules/Editor.js
new file mode 100644
index 0000000..3ed571c
--- /dev/null
+++ b/src/modules/Editor.js
@@ -0,0 +1,57 @@
+import { Map } from '$modules/Map';
+import { Poly } from "$modules/Poly";
+import { MODES } from '$constants/modes';
+import { Stickers } from '$modules/Stickers';
+
+export class Editor {
+ constructor({
+ container,
+ mode,
+ setMode
+ }) {
+ this.map = new Map({ container });
+ this.poly = new Poly({ map: this.map.map });
+ this.stickers = new Stickers({ map: this.map.map });
+
+ this.setMode = setMode;
+ this.mode = mode;
+
+ this.switches = {
+ [MODES.POLY]: {
+ start: this.poly.continue,
+ stop: this.poly.stop,
+ }
+ };
+
+ this.clickHandlers = {
+ [MODES.STICKERS]: this.stickers.createOnClick
+ };
+
+ this.map.map.on('click', this.onClick);
+ }
+
+ changeMode = mode => {
+ if (this.mode === mode) {
+ this.disableMode(mode);
+ this.setMode(MODES.NONE);
+ this.mode = MODES.NONE;
+ } else {
+ this.disableMode(this.mode);
+ this.setMode(mode);
+ this.mode = mode;
+ this.enableMode(mode);
+ }
+ };
+
+ enableMode = mode => {
+ if (this.switches[mode] && this.switches[mode].start) this.switches[mode].start();
+ };
+
+ disableMode = mode => {
+ if (this.switches[mode] && this.switches[mode].stop) this.switches[mode].stop();
+ };
+
+ onClick = e => {
+ if (this.clickHandlers[this.mode]) this.clickHandlers[this.mode](e);
+ };
+}
diff --git a/src/modules/Map.js b/src/modules/Map.js
new file mode 100644
index 0000000..ee001d2
--- /dev/null
+++ b/src/modules/Map.js
@@ -0,0 +1,19 @@
+import L from 'leaflet';
+import { providers } from '$constants/providers';
+
+import 'leaflet/dist/leaflet.css';
+import 'leaflet-editable';
+
+export class Map {
+ constructor({ container }) {
+ this.map = L.map(container, { editable: true }).setView([55.0153275, 82.9071235], 13);
+
+ this.tileLayer = L.tileLayer(providers.default, {
+ attribution: 'Независимое Велосообщество',
+ maxNativeZoom: 18,
+ maxZoom: 18,
+ });
+
+ this.tileLayer.addTo(this.map);
+ }
+}
diff --git a/src/modules/Poly.js b/src/modules/Poly.js
new file mode 100644
index 0000000..dd7fabb
--- /dev/null
+++ b/src/modules/Poly.js
@@ -0,0 +1,70 @@
+import L from "leaflet";
+
+const polyStyle = { color: '#ff3333', weight: '5' };
+
+export class Poly {
+ constructor({ map }) {
+ this.poly = L.polyline([], polyStyle);
+ this.latlngs = [];
+ this.poly.addTo(map);
+ this.map = map;
+
+ this.bindEvents();
+ }
+
+ updateMarks = () => {
+ const coords = this.poly.toGeoJSON().geometry.coordinates;
+ this.latlngs = (coords && coords.length && coords.map(([lng, lat]) => ({ lng, lat }))) || [];
+ };
+
+ bindEvents = () => {
+ // Если на карте что-то меняется, пересчитать километражи
+ this.map.editTools.addEventListener('editable:drawing:mouseup', this.updateMarks);
+ this.map.editTools.addEventListener('editable:vertex:dragend', this.updateMarks);
+ this.map.editTools.addEventListener('editable:vertex:deleted', this.updateMarks);
+ this.map.editTools.addEventListener('editable:vertex:new', this.updateMarks);
+
+ // После удаления точки - продолжить рисование
+ this.map.editTools.addEventListener('editable:vertex:deleted', this.continueForward);
+ //
+ // map.editTools.addEventListener('editable:vertex:dragend', e => writeReduxData({ e, updatePolyCoords }));
+ // map.editTools.addEventListener('editable:vertex:new', e => writeReduxData({ e, updatePolyCoords }));
+ // map.editTools.addEventListener('editable:vertex:deleted', e => writeReduxData({ e, updatePolyCoords }));
+
+ // Продолжить рисование после удаления точки
+ // map.editTools.addEventListener('editable:vertex:deleted', e => {
+ // poly.editor.continueForward();
+ // updateMarks();
+ // });
+
+ // Добавлять точек в полилинию по щелчку
+ // map.editTools.addEventListener('editable:drawing:click', e => insertVertex({ e, updatePolyCoords }));
+ // map.editTools.addEventListener('editable:drawing:clicked', () => updateMarks({ updatePolyCoords }));
+
+ // Это для точек. При перетаскивании конца указателя тащим точку
+ // map.editTools.addEventListener('editable:vertex:drag', on_vertex_drag);
+
+ // при перетаскивании ручек убирать все отметки километров
+ // map.editTools.addEventListener('editable:vertex:dragstart', clearKmMarks);
+ };
+
+ continue = () => {
+ if (this.latlngs && this.latlngs.length) {
+ this.poly.enableEdit().continueForward();
+ this.poly.editor.options.skipMiddleMarkers = true;
+ this.poly.editor.reset();
+ } else {
+ this.poly = this.map.editTools.startPolyline();
+ this.poly.setStyle(polyStyle);
+ }
+ };
+
+ stop = () => {
+ if (this.map.editTools) this.map.editTools.stopDrawing();
+ };
+
+ continueForward = () => {
+ if (!this.poly.editor) return;
+ this.poly.editor.continueForward();
+ };
+}
diff --git a/src/modules/Sticker.js b/src/modules/Sticker.js
new file mode 100644
index 0000000..3714514
--- /dev/null
+++ b/src/modules/Sticker.js
@@ -0,0 +1,49 @@
+import L from 'leaflet';
+import 'leaflet-editable';
+
+import { DomMarker } from '$utils/leafletDomMarkers';
+
+export class Sticker {
+ constructor({ latlng, deleteSticker }) {
+ this.isDragging = false;
+
+ this.deleteSticker = deleteSticker;
+ this.element = document.createElement('div');
+
+ const stickerImage = document.createElement('div');
+ stickerImage.innerHTML = '';
+
+ const stickerArrow = document.createElement('div');
+ stickerArrow.innerHTML = '';
+
+ this.element.appendChild(stickerArrow);
+ this.element.appendChild(stickerImage);
+
+ const marker = new DomMarker({
+ element: this.element,
+ });
+
+ this.sticker = L.marker(latlng, { icon: marker });
+
+ stickerImage.addEventListener('mousedown', this.onDragStart);
+ stickerImage.addEventListener('mouseup', this.onDragStop);
+ //
+ // this.sticker.addEventListener('click', this.onDelete);
+ }
+
+ onDelete = () => {
+ if (!this.isDragging) this.deleteSticker(this);
+ };
+
+ onDragStart = e => {
+ this.isDragging = true;
+ this.sticker.disableEdit();
+ console.log('dragStart');
+ };
+
+ onDragStop = e => {
+ this.isDragging = false;
+ this.sticker.enableEdit();
+ console.log('dragStop');
+ }
+}
diff --git a/src/modules/Stickers.js b/src/modules/Stickers.js
new file mode 100644
index 0000000..532c7dc
--- /dev/null
+++ b/src/modules/Stickers.js
@@ -0,0 +1,40 @@
+import L from 'leaflet';
+import { Sticker } from '$modules/Sticker';
+
+export class Stickers {
+ constructor({ map }) {
+ this.map = map;
+ this.layer = L.layerGroup();
+
+ this.stickers = [];
+
+ this.layer.addTo(this.map);
+ }
+
+ createOnClick = e => {
+ if (!e || !e.latlng) return;
+
+ const { latlng } = e;
+ this.createSticker({ latlng });
+ };
+
+ createSticker = ({ latlng }) => {
+ const sticker = new Sticker({
+ latlng,
+ deleteSticker: this.deleteStickerByReference,
+ });
+ this.stickers.push(sticker);
+
+ sticker.sticker.addTo(this.map);
+ sticker.sticker.enableEdit();
+ };
+
+ deleteStickerByReference = ref => {
+ const index = this.stickers.indexOf(ref);
+
+ if (index < 0) return;
+
+ this.map.removeLayer(ref.sticker);
+ this.stickers.splice(index, 1);
+ };
+}
diff --git a/src/modules/map.js b/src/modules/map.js
deleted file mode 100644
index 5eface5..0000000
--- a/src/modules/map.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import L from "leaflet";
-
-import { providers } from "$constants/providers";
-
-import 'leaflet/dist/leaflet.css';
-
-export class Map {
- constructor(container) {
- this.map = L.map(container, {
- editable: true,
- layers: [
-
- ]
- }).setView([55.0153275, 82.9071235], 13);
-
- this.tileLayer = L.tileLayer(providers.default, {
- attribution: 'Независимое Велосообщество',
- maxNativeZoom: 18,
- maxZoom: 18,
- }).addTo(this.map);
- }
-}
diff --git a/src/parts/map.js b/src/parts/map.js
index 462bd7f..8bffded 100644
--- a/src/parts/map.js
+++ b/src/parts/map.js
@@ -1,16 +1,16 @@
-import L from "leaflet";
+import L from 'leaflet';
import 'leaflet-editable';
import 'leaflet.markercluster';
import 'leaflet.markercluster.webpack';
import 'leaflet-geometryutil';
-import { mapStyles } from "$constants/mapStyles";
+import { mapStyles } from '$constants/mapStyles';
-import { stickers } from "$constants/stickers";
+import { stickers } from '$constants/stickers';
-import { updateMarks } from "$utils/updater";
-import { bindPolyEvents, preparePoly } from "$utils/poly";
+import { updateMarks } from '$utils/updater';
+import { bindPolyEvents, preparePoly } from '$utils/poly';
// В этой штуке мы храним точки и выноски, их связки и всё такое
const point_array = {
@@ -25,15 +25,15 @@ const point_array = {
const points = L.layerGroup();
-let mode = "none";
-let current_map_style = 'default';
+let mode = 'none';
+const current_map_style = 'default';
// Интересные места;
// const places_layer;
export const map = L.map('map', {
editable: true,
- layers: [ points, point_array.points, point_array.vectors, stickers.layers ]
+ layers: [points, point_array.points, point_array.vectors, stickers.layers]
}).setView([55.0153275, 82.9071235], 13);
map.editTools.skipMiddleMarkers = true;
@@ -51,12 +51,11 @@ const prepareMapLayer = provider => {
attribution: 'Независимое Велосообщество',
maxNativeZoom: 18,
maxZoom: 18,
- //minZoom: 11
+ // minZoom: 11
}).addTo(map);
};
const bindMapEvents = () => {
-
// при масштабировании карты масштабировать стрелки
// map.on('zoom', function (e) {
// $('.arr_mark > div').css('transform', 'scale(' + (map.getZoom()/13) + ')');
diff --git a/src/parts/poly.js b/src/parts/poly.js
index 6e26768..beac0c0 100644
--- a/src/parts/poly.js
+++ b/src/parts/poly.js
@@ -200,7 +200,7 @@ const createPoly = () => {
};
const restorePoly = latlngs => {
- const result = L.polyline(createLatLngs(latlngs), {color: 'red'}).addTo(map);
+ const result = L.polyline(createLatLngs(latlngs), { color: 'red' }).addTo(map);
result.enableEdit().continueForward();
result.editor.options.skipMiddleMarkers = true;
diff --git a/src/styles/controlsScreen.js b/src/styles/controlsScreen.js
new file mode 100644
index 0000000..8d07ef4
--- /dev/null
+++ b/src/styles/controlsScreen.js
@@ -0,0 +1,13 @@
+import styled from 'styled-components';
+
+export const ControlsScreen = styled.div`
+ position: fixed;
+ right: 10px;
+ bottom: 10px;
+ height: 48px;
+ min-width: 120px;
+ background: #333333;
+ border-radius: 2px;
+ z-index: 2;
+ color: white;
+`;
diff --git a/src/styles/mapScreen.js b/src/styles/mapScreen.js
new file mode 100644
index 0000000..5fe381f
--- /dev/null
+++ b/src/styles/mapScreen.js
@@ -0,0 +1,60 @@
+import styled, { css } from 'styled-components';
+
+const vertexMixin = css`
+ .leaflet-vertex-icon, .leaflet-middle-icon {
+ border-radius: 10px;
+ opacity :1;
+ border: none;
+ width: 16px !important;
+ height: 16px !important;margin-left:-8px !important;margin-top:-8px !important;
+ background: transparent;
+ }
+
+ .leaflet-vertex-icon::after, .leaflet-middle-icon::after {
+ content: ' ';
+ position:absolute;top:4px;left:4px;width:8px;height:8px;
+ background:white;border-radius: 8px;transform:scale(1);
+ transition:transform 150ms;
+ }
+
+ .leaflet-vertex-icon:hover, .leaflet-middle-icon:hover {
+ opacity: 1 !important;
+ }
+
+ .leaflet-vertex-icon:hover::after, .leaflet-middle-icon:hover::after,
+ .leaflet-vertex-icon:active::after, .leaflet-middle-icon:active::after {
+ transform: scale(2);
+ box-shadow: #999 0 0 5px 2px;
+ }
+`;
+
+const stickers = css`
+ .sticker-label {
+ width: 48px;
+ height: 48px;
+ position: absolute;
+ background: white;
+ border-radius: 32px;
+ left: 0;
+ top: 0;
+ }
+
+ .sticker-arrow {
+ width: 24px;
+ height: 24px;
+ position: absolute;
+ background: red;
+ }
+`;
+
+export const MapScreen = styled.div.attrs({ id: 'map' })`
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ z-index: 1;
+ left: 0;
+ top: 0;
+
+ ${vertexMixin}
+ ${stickers}
+`;
diff --git a/src/utils/leafletDomMarkers.js b/src/utils/leafletDomMarkers.js
new file mode 100644
index 0000000..19b27e7
--- /dev/null
+++ b/src/utils/leafletDomMarkers.js
@@ -0,0 +1,21 @@
+import L from 'leaflet';
+
+export const DomMarker = L.DivIcon.extend({
+ initialize: function (options) {
+ this.options = options;
+ },
+
+ options: {
+ element: null // a initialized DOM element
+ // same options as divIcon except for html
+ },
+
+ createIcon: function() {
+ const { html, element } = this.options;
+
+ this._setIconStyles(element, 'icon');
+
+ return element;
+ }
+});
+
diff --git a/webpack.config.js b/webpack.config.js
index 25a3c5a..9a81f1d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -13,7 +13,7 @@ const { join } = require('path');
const htmlPlugin = new HtmlWebPackPlugin({
template: './src/index.html',
filename: './index.html',
- title: 'Ether Corners',
+ title: 'Map',
hash: false,
});