Delivery cost calculator
vanilla.html
react.html
vue.html
common.css
common.ts
variables.ts
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/@turf/turf@7.1/turf.min.js"></script>
<!-- To make the map appear, you must add your apikey -->
<script src="https://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="../variables.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="./common.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
import {
LOCATION,
ROUTE_START,
OUT_OF_ZONES_PRICE,
ROUTE_STYLES,
TRANSLATIONS,
ZONES,
END_MARKER_COLOR,
START_MARKER_COLOR
} from '../variables';
import type {DomEventHandler, RouteFeature} from '@yandex/ymaps3-types';
import {calculatePrice, fetchRoute, type MapZone} from './common';
window.map = null;
interface InfoMessageProps {
text: string;
}
interface DeliverySumControlProps {
currentZone: MapZone;
outOfZoneLineLength: number;
price: number;
}
main();
async function main() {
// Waiting for all api elements to be loaded
await ymaps3.ready;
const {
YMap,
YMapDefaultSchemeLayer,
YMapDefaultFeaturesLayer,
YMapFeature,
YMapListener,
YMapControls,
YMapControl
} = ymaps3;
const {YMapDefaultMarker, YMapSearchControl} = await ymaps3.import('@yandex/ymaps3-default-ui-theme');
class InfoMessageClass extends ymaps3.YMapComplexEntity<InfoMessageProps> {
private _element!: HTMLDivElement;
private _detachDom!: () => void;
// Method for create a DOM control element
_createElement(props: InfoMessageProps) {
// Create a root element
const infoWindow = document.createElement('div');
infoWindow.classList.add('info-window');
infoWindow.innerHTML = props.text;
return infoWindow;
}
// Method for attaching the control to the map
_onAttach() {
this._element = this._createElement(this._props);
this._detachDom = ymaps3.useDomContext(this, this._element, this._element);
}
// Method for detaching control from the map
_onDetach() {
this._detachDom();
this._detachDom = undefined;
this._element = undefined;
}
}
class DeliveryCostControl extends ymaps3.YMapComplexEntity<{}> {
private _element!: HTMLDivElement;
private _detachDom!: () => void;
// Method for create a DOM control element
_createElement() {
// Create a root element
const windowElement = document.createElement('div');
windowElement.classList.add('delivery-cost-window');
const windowTitle = document.createElement('div');
windowTitle.classList.add('delivery-cost-title');
windowTitle.innerText = TRANSLATIONS.deliveryWindowTitle;
const windowContent = document.createElement('div');
windowContent.classList.add('delivery-cost-content');
for (const zone of ZONES) {
const zoneItem = document.createElement('div');
zoneItem.classList.add('delivery-item');
const colorBox = document.createElement('div');
colorBox.classList.add('delivery-item-colorbox');
colorBox.style.backgroundColor = zone.style.fill;
colorBox.style.borderColor = zone.style.stroke[0].color;
const text = document.createElement('div');
text.innerText = `${zone.name} — ${zone.price} ${TRANSLATIONS.currency}`;
zoneItem.appendChild(colorBox);
zoneItem.appendChild(text);
windowContent.appendChild(zoneItem);
}
const divider = document.createElement('hr');
divider.classList.add('divider');
const windowFooter = document.createElement('div');
windowFooter.classList.add('delivery-cost-footer');
windowFooter.innerText = `${TRANSLATIONS.deliveryWindowFooter} ${OUT_OF_ZONES_PRICE} ${TRANSLATIONS.currency}`;
windowElement.appendChild(windowTitle);
windowElement.appendChild(windowContent);
windowElement.appendChild(divider);
windowElement.appendChild(windowFooter);
return windowElement;
}
// Method for attaching the control to the map
_onAttach() {
this._element = this._createElement();
this._detachDom = ymaps3.useDomContext(this, this._element, this._element);
}
// Method for detaching control from the map
_onDetach() {
this._detachDom();
this._detachDom = undefined;
this._element = undefined;
}
}
class DeliverySumControl extends ymaps3.YMapComplexEntity<{}> {
private _element!: HTMLDivElement;
private _detachDom!: () => void;
private contentElement: HTMLDivElement;
private footerElement: HTMLDivElement;
// Method for create a DOM control element
_createElement() {
// Create a root element
const windowElement = document.createElement('div');
windowElement.classList.add('delivery-sum-window');
const windowTitle = document.createElement('div');
windowTitle.classList.add('delivery-sum-title');
windowTitle.innerText = TRANSLATIONS.deliverySumTitle;
const windowContent = document.createElement('div');
windowContent.classList.add('delivery-sum-content');
windowContent.id = 'delivery-sum-content';
windowElement.appendChild(windowTitle);
windowElement.appendChild(windowContent);
const windowFooter = document.createElement('div');
windowFooter.classList.add('delivery-sum-footer');
windowFooter.id = 'delivery-sum-footer';
windowElement.appendChild(windowFooter);
this.contentElement = windowContent;
this.footerElement = windowFooter;
return windowElement;
}
update(changedProps: Partial<DeliverySumControlProps>) {
this.contentElement.innerText = `${
!changedProps.outOfZoneLineLength && changedProps.currentZone ? `${changedProps.currentZone.name}` : ''
} ${changedProps.price.toFixed()} ${TRANSLATIONS.currency}`;
if (changedProps.outOfZoneLineLength) {
this.footerElement.classList.remove('hidden');
} else {
this.footerElement.classList.add('hidden');
}
this.footerElement.innerText = `${changedProps.currentZone.name} ${
!!changedProps.outOfZoneLineLength
? `+ ${changedProps.outOfZoneLineLength.toFixed()}${TRANSLATIONS.units}`
: ''
}`;
}
// Method for attaching the control to the map
_onAttach() {
this._element = this._createElement();
this._detachDom = ymaps3.useDomContext(this, this._element, this._element);
}
// Method for detaching control from the map
_onDetach() {
this._detachDom();
this._detachDom = undefined;
this._element = undefined;
}
}
// Initialize the map
map = new YMap(
// Pass the link to the HTMLElement of the container
document.getElementById('app'),
// Pass the map initialization parameters
{location: LOCATION, showScaleInCopyrights: true},
// Add a map scheme layer
[new YMapDefaultSchemeLayer({}), new YMapDefaultFeaturesLayer({})]
);
const rerenderComponents = ({price, outOfZoneLineLength, currentZone, coordinates, routeGeometry}) => {
if (!route.parent) {
map.addChild(route);
}
route.update({geometry: routeGeometry});
if (!marker.parent) {
map.addChild(marker);
}
marker.update({coordinates});
if (!deliverySumControl.parent) {
leftControl.addChild(deliverySumControl);
}
deliverySumControl.update({currentZone, price, outOfZoneLineLength});
};
/* A handler function that updates the route line
and shifts the map to the new route boundaries, if they are available. */
const routeHandler = (newRoute: RouteFeature) => {
const props = calculatePrice(newRoute);
return {
...props,
routeGeometry: newRoute.geometry
};
};
const searchHandler = (searchResults) => {
fetchRoute(ROUTE_START, searchResults[0].geometry.coordinates).then((route) => {
const renderProps = routeHandler(route);
rerenderComponents({...renderProps, coordinates: searchResults[0].geometry.coordinates});
});
};
const onMapClick: DomEventHandler = (object, event) => {
fetchRoute(ROUTE_START, event.coordinates).then((route) => {
const renderProps = routeHandler(route);
rerenderComponents({...renderProps, coordinates: event.coordinates});
});
};
const route = new YMapFeature({
geometry: {type: 'LineString', coordinates: []},
style: ROUTE_STYLES
});
const deliverySumControl = new DeliverySumControl({});
const marker = new YMapDefaultMarker({
coordinates: ROUTE_START,
iconName: 'building',
size: 'normal',
color: {day: END_MARKER_COLOR, night: END_MARKER_COLOR}
});
ZONES.forEach((zone) => map.addChild(new YMapFeature(zone)));
map.addChild(
new YMapDefaultMarker({
coordinates: ROUTE_START,
iconName: 'malls',
size: 'normal',
color: {day: START_MARKER_COLOR, night: START_MARKER_COLOR}
})
);
map.addChild(
new YMapControls({position: 'top right'}, [
new YMapSearchControl({
searchResult: searchHandler
})
])
);
const leftControl = new YMapControls({position: 'top left', orientation: 'vertical'}, [
new YMapControl({transparent: true}).addChild(new InfoMessageClass({text: TRANSLATIONS.tooltip})),
new YMapControl({transparent: true}).addChild(new DeliveryCostControl({}))
]);
map.addChild(leftControl);
map.addChild(new YMapListener({onClick: onMapClick}));
}
</script>
<!-- prettier-ignore -->
<style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; } </style>
<link rel="stylesheet" href="./common.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@17/umd/react-dom.production.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/@turf/turf@7.1/turf.min.js"></script>
<!-- To make the map appear, you must add your apikey -->
<script src="https://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>
<script
data-plugins="transform-modules-umd"
data-presets="react, typescript"
type="text/babel"
src="../variables.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="react, typescript"
type="text/babel"
src="./common.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
import {
LOCATION,
OUT_OF_ZONES_PRICE,
ROUTE_START,
ROUTE_STYLES,
TRANSLATIONS,
ZONES,
END_MARKER_COLOR,
START_MARKER_COLOR
} from '../variables';
import type {DomEventHandler, LngLat, RouteFeature, LineStringGeometry} from '@yandex/ymaps3-types';
import {calculatePrice, fetchRoute, type MapZone} from './common';
window.map = null;
main();
async function main() {
// For each object in the JS API, there is a React counterpart
// To use the React version of the API, include the module @yandex/ymaps3-reactify
const [ymaps3React] = await Promise.all([ymaps3.import('@yandex/ymaps3-reactify'), ymaps3.ready]);
const reactify = ymaps3React.reactify.bindTo(React, ReactDOM);
const {
YMap,
YMapDefaultSchemeLayer,
YMapDefaultFeaturesLayer,
YMapFeature,
YMapListener,
YMapControls,
YMapControl
} = reactify.module(ymaps3);
const {YMapDefaultMarker, YMapSearchControl} = reactify.module(
await ymaps3.import('@yandex/ymaps3-default-ui-theme')
);
const {useState, useCallback} = React;
function App() {
const [price, setPrice] = useState(null);
const [currentZone, setCurrentZone] = useState < MapZone > null;
const [outOfZoneLineLength, setOutOfZoneLineLength] = useState(null);
const [finishCoordinates, setFinishCoordinates] = useState < LngLat > null;
const [routeGeometry, setRouteGeometry] = useState < LineStringGeometry > null;
const onMapClick: DomEventHandler = useCallback((object, event) => {
setFinishCoordinates(event.coordinates);
fetchRoute(ROUTE_START, event.coordinates).then((route) => routeHandler(route));
}, []);
/* A handler function that updates the route line
and shifts the map to the new route boundaries, if they are available. */
const routeHandler = useCallback((newRoute: RouteFeature) => {
setRouteGeometry(newRoute.geometry);
const {outOfZoneLineLength, price, currentZone} = calculatePrice(newRoute);
setPrice(price);
setOutOfZoneLineLength(outOfZoneLineLength);
setCurrentZone(currentZone);
}, []);
const searchHandler = (searchResults) => {
setFinishCoordinates(searchResults[0].geometry.coordinates);
fetchRoute(ROUTE_START, searchResults[0].geometry.coordinates).then((route) => routeHandler(route));
};
return (
// Initialize the map and pass initialization parameters
<YMap location={LOCATION} showScaleInCopyrights={true} ref={(x) => (map = x)}>
{/* Add a map scheme layer */}
<YMapDefaultSchemeLayer />
<YMapDefaultFeaturesLayer />
{ZONES.map((zone) => (
<YMapFeature key={zone.name} style={zone.style} geometry={zone.geometry} />
))}
{routeGeometry && <YMapFeature style={ROUTE_STYLES} geometry={routeGeometry} />}
<YMapDefaultMarker
color={{day: START_MARKER_COLOR, night: START_MARKER_COLOR}}
size="normal"
iconName="malls"
coordinates={ROUTE_START}
/>
{finishCoordinates && (
<YMapDefaultMarker
size="normal"
color={{day: END_MARKER_COLOR, night: END_MARKER_COLOR}}
iconName="building"
coordinates={finishCoordinates}
/>
)}
<YMapControls position="top right">
<YMapSearchControl searchResult={searchHandler} />
</YMapControls>
<YMapControls position="top left" orientation="vertical">
<YMapControl transparent>
<div className="info-window">{TRANSLATIONS.tooltip}</div>
</YMapControl>
<YMapControl transparent>
<div className="delivery-cost-window">
<div className="delivery-cost-title">{TRANSLATIONS.deliveryWindowTitle}</div>
<div className="delivery-cost-content">
{ZONES.map((zone) => (
<div key={zone.name} className="delivery-item">
<div
className="delivery-item-colorbox"
style={{
backgroundColor: zone.style.fill,
borderColor: zone.style.stroke[0].color
}}
/>
<div>
{zone.name} — {zone.price} {TRANSLATIONS.currency}
</div>
</div>
))}
</div>
<hr className="divider" />
<div className="delivery-cost-footer">
{TRANSLATIONS.deliveryWindowFooter} {OUT_OF_ZONES_PRICE} {TRANSLATIONS.currency}
</div>
</div>
</YMapControl>
{currentZone && price && (
<YMapControl transparent>
<div className="delivery-sum-window">
<div className="delivery-sum-title">{TRANSLATIONS.deliverySumTitle}</div>
<div className="delivery-sum-content">
{!outOfZoneLineLength && currentZone ? `${currentZone.name}` : ''} {price.toFixed()}
{TRANSLATIONS.currency}
</div>
{outOfZoneLineLength && (
<div className="delivery-sum-footer">
{currentZone.name} + {outOfZoneLineLength.toFixed()}
{TRANSLATIONS.units}
</div>
)}
</div>
</YMapControl>
)}
</YMapControls>
<YMapListener onClick={onMapClick} />
</YMap>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('app')
);
}
</script>
<!-- prettier-ignore -->
<style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; } </style>
<link rel="stylesheet" href="./common.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/@turf/turf@7.1/turf.min.js"></script>
<script src="https://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="../variables.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="./common.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel"></script>
<!-- prettier-ignore -->
<style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; } </style>
<link rel="stylesheet" href="./common.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
.info-window {
padding: 8px 12px 8px 40px;
border-radius: 12px;
background-color: #313133;
background-image: url('./info-icon.svg');
background-position: 10px 8px;
background-repeat: no-repeat;
color: #f2f5fa;
font-size: 14px;
line-height: 20px;
min-width: max-content;
}
.delivery-sum-window {
width: 220px;
padding: 10px 12px;
background-color: #212326;
border-radius: 12px;
box-sizing: border-box;
}
.delivery-sum-title {
color: #ffffff;
font-size: 14px;
font-weight: 400;
}
.delivery-sum-content {
margin-top: 8px;
font-weight: 500;
font-size: 16px;
color: #ffffff;
}
.delivery-sum-footer {
font-weight: 500;
font-size: 14px;
color: #f2f5fa;
opacity: 0.7;
}
.delivery-cost-window {
margin-top: 16px;
width: 220px;
padding: 8px;
background-color: #ffffff;
border-radius: 12px;
box-sizing: border-box;
box-shadow: 0px 4px 12px 0px #5f69831a;
}
.delivery-cost-title {
height: 40px;
padding: 8px;
font-weight: 500;
font-size: 16px;
box-sizing: border-box;
}
.delivery-cost-content {
padding: 8px;
display: flex;
flex-direction: column;
gap: 8px;
}
.delivery-cost-footer {
box-sizing: border-box;
padding: 4px 8px 8px 8px;
height: 32px;
font-size: 14px;
}
.delivery-item {
display: flex;
flex-direction: row;
gap: 12px;
}
.delivery-item-colorbox {
border-style: solid;
border-width: 3px;
box-sizing: border-box;
width: 20px;
height: 20px;
border-radius: 4px;
}
.divider {
border-top: 1px solid rgba(92, 94, 102, 0.14);
border-bottom: none;
border-radius: 8px;
margin: 8px;
}
.hidden {
display: none;
}
import type {DrawingStyle, LineStringGeometry, LngLat, PolygonGeometry, RouteFeature} from '@yandex/ymaps3-types';
import {Feature} from 'geojson';
import {OUT_OF_ZONES_PRICE, ZONES} from './variables';
export type MapZone = {
style: DrawingStyle;
geometry: PolygonGeometry;
price: number;
priority: number;
name: string;
};
// Wait for the api to load to access the map configuration
ymaps3.ready.then(() => {
// Copy your api key for routes from the developer's dashboard and paste it here
ymaps3.getDefaultConfig().setApikeys({router: '<YOUR_APIKEY>'});
ymaps3.import.registerCdn('https://cdn.jsdelivr.net/npm/{package}', '@yandex/ymaps3-default-ui-theme@0.0');
});
export async function fetchRoute(startCoordinates: LngLat, endCoordinates: LngLat) {
// Request a route from the Router API with the specified parameters.
const routes = await ymaps3.route({
points: [startCoordinates, endCoordinates], // Start and end points of the route LngLat[]
type: 'driving', // Type of the route
bounds: true // Flag indicating whether to include route boundaries in the response
});
// Check if a route was found
if (!routes[0]) return;
// Convert the received route to a RouteFeature object.
const route = routes[0].toRoute();
// Check if a route has coordinates
if (route.geometry.coordinates.length == 0) return;
return route;
}
export function getLineStringLength(geometry: LineStringGeometry) {
const feature: Feature = {
type: 'Feature',
geometry,
properties: {}
};
return turf.length(feature);
}
export function getOutOfZoneLineSlice(route: LineStringGeometry, zone: PolygonGeometry) {
const splitPoints = turf.lineIntersect(zone, route);
const outOfZoneLineSlice = turf.lineSlice(
splitPoints.features[0].geometry.coordinates,
route.coordinates[route.coordinates.length - 1],
route
);
return outOfZoneLineSlice;
}
export function calculatePrice(route: RouteFeature) {
let price: number;
let outOfZoneLineLength: number;
const finalPoint = route.geometry.coordinates[route.geometry.coordinates.length - 1];
const sortedZones = ZONES.sort((a, b) => b.priority - a.priority);
let currentZone: MapZone = null;
for (const zone of sortedZones) {
const pointIsInZones = turf.booleanPointInPolygon(finalPoint, zone.geometry);
if (pointIsInZones) {
currentZone = zone;
}
}
if (currentZone) {
price = currentZone.price;
} else {
const lastZone = sortedZones[0];
const outOfZoneLineSlice = getOutOfZoneLineSlice(route.geometry, lastZone.geometry);
outOfZoneLineLength = getLineStringLength(outOfZoneLineSlice.geometry as LineStringGeometry);
price = lastZone.price + OUT_OF_ZONES_PRICE * outOfZoneLineLength;
currentZone = lastZone;
}
return {
price,
outOfZoneLineLength,
currentZone
};
}
import type {DrawingStyle, LngLat, YMapLocationRequest} from '@yandex/ymaps3-types';
import {MapZone} from './common';
export const LOCATION: YMapLocationRequest = {
center: [37.6225, 55.7536], // starting position [lng, lat]
zoom: 11.2 // starting zoom
};
export const ROUTE_START: LngLat = [37.6225, 55.7536];
export const END_MARKER_COLOR = '#313133';
export const START_MARKER_COLOR = '#EB5547';
export const ROUTE_STYLES: DrawingStyle = {
simplificationRate: 0,
stroke: [
{color: '#83C753', width: 6},
{color: '#000000', width: 8, opacity: 0.3}
],
fill: '#83C753'
};
export const ZONES: Array<MapZone> = [
{
style: {
simplificationRate: 0,
stroke: [{color: '#EF9A7A', width: 3}],
fill: 'rgba(239, 154, 122, 0.29)'
},
geometry: {
type: 'Polygon',
coordinates: [
[
[37.6329, 55.7727],
[37.6371, 55.7723],
[37.6496, 55.7688],
[37.6562, 55.7644],
[37.6578, 55.7613],
[37.6573, 55.7549],
[37.656, 55.7526],
[37.6549, 55.7427],
[37.6523, 55.7405],
[37.6417, 55.7332],
[37.6376, 55.7313],
[37.6237, 55.7296],
[37.6122, 55.7298],
[37.586, 55.7384],
[37.5826, 55.7466],
[37.583, 55.7524],
[37.5848, 55.7589],
[37.5887, 55.7642],
[37.5947, 55.7687],
[37.6007, 55.7714],
[37.6065, 55.7729],
[37.6175, 55.7737],
[37.6267, 55.7735],
[37.6329, 55.7727]
]
]
},
price: 300,
priority: 1,
name: 'зона A'
},
{
style: {
simplificationRate: 0,
stroke: [{color: '#EE5441', width: 3}],
fill: 'rgba(238, 84, 65, 0.1)'
},
geometry: {
type: 'Polygon',
coordinates: [
[
[37.5839, 55.7093],
[37.587, 55.7072],
[37.6093, 55.7009],
[37.6141, 55.7017],
[37.6187, 55.7049],
[37.6224, 55.7059],
[37.6566, 55.7031],
[37.6725, 55.7117],
[37.7111, 55.7232],
[37.7115, 55.7256],
[37.6992, 55.7362],
[37.6971, 55.7401],
[37.6984, 55.7438],
[37.6999, 55.7474],
[37.6923, 55.7555],
[37.6863, 55.7581],
[37.685, 55.7597],
[37.6847, 55.7615],
[37.6848, 55.7633],
[37.6888, 55.7693],
[37.6881, 55.7713],
[37.6818, 55.7765],
[37.67, 55.7812],
[37.6632, 55.7854],
[37.6524, 55.7934],
[37.6471, 55.794],
[37.6353, 55.7921],
[37.6292, 55.7921],
[37.6175, 55.7933],
[37.5744, 55.7916],
[37.5657, 55.7854],
[37.5579, 55.78],
[37.554, 55.7753],
[37.5522, 55.7744],
[37.5461, 55.7734],
[37.5423, 55.7704],
[37.5377, 55.7666],
[37.5343, 55.759],
[37.5309, 55.7516],
[37.5353, 55.7404],
[37.5423, 55.7342],
[37.5473, 55.7278],
[37.5503, 55.7243],
[37.5574, 55.7213],
[37.5693, 55.7177],
[37.576, 55.715],
[37.5839, 55.7093]
]
]
},
price: 500,
priority: 2,
name: 'зона B'
},
{
style: {
simplificationRate: 0,
stroke: [{color: '#709FD3', width: 3}],
fill: 'rgba(112, 159, 211, 0.1)'
},
geometry: {
type: 'Polygon',
coordinates: [
[
[37.8385, 55.6577],
[37.8298, 55.6919],
[37.8353, 55.707],
[37.8393, 55.7146],
[37.8447, 55.7776],
[37.8399, 55.8126],
[37.8376, 55.8241],
[37.7288, 55.8812],
[37.7067, 55.8914],
[37.6933, 55.895],
[37.6523, 55.8958],
[37.5931, 55.9077],
[37.5766, 55.9113],
[37.5387, 55.9073],
[37.5222, 55.9037],
[37.478, 55.886],
[37.4662, 55.883],
[37.4473, 55.8825],
[37.4134, 55.8719],
[37.4008, 55.8631],
[37.3929, 55.8467],
[37.3952, 55.8344],
[37.3858, 55.8038],
[37.3716, 55.7891],
[37.3693, 55.7488],
[37.3858, 55.7137],
[37.4118, 55.6902],
[37.426, 55.6693],
[37.4583, 55.6391],
[37.4961, 55.6088],
[37.5103, 55.5959],
[37.5994, 55.5753],
[37.6691, 55.5719],
[37.6797, 55.5731],
[37.7431, 55.5972],
[37.7881, 55.6204],
[37.8267, 55.6451],
[37.8385, 55.6577]
]
]
},
price: 900,
priority: 3,
name: 'зона C'
}
];
export const OUT_OF_ZONES_PRICE = 200;
export const TRANSLATIONS = {
deliveryWindowTitle: 'Стоимость доставки',
deliveryWindowFooter: 'Каждый доп. 1км + ',
deliverySumTitle: 'Ваша доставка',
units: 'км',
currency: '₽',
tooltip: 'Выберите адрес доставки на карте или через поиск'
};