Line with dashes
vanilla.html
react.html
vue.html
common.css
variables.css
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>
<!-- To make the map appear, you must add your apikey -->
<script src="https://js.api.mappable.world/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="typescript" type="text/babel">
import {FEATURE_GEOMETRY, LOCATION, STROKE_COLOR} from '../variables';
window.map = null;
main();
async function main() {
// Waiting for all api elements to be loaded
await mappable.ready;
const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapFeature, MMapControls, MMapControl} = mappable;
const lineStyles = {
width: 4,
gap: 8,
dash: 8
};
class DashStyleControl extends mappable.MMapComplexEntity<{}> {
private _element!: HTMLDivElement;
private _detachDom!: () => void;
// Method for create a DOM control element
_createElement() {
// Create a root element
const container = document.createElement('div');
container.classList.add('container');
['width', 'dash', 'gap'].forEach((key) => {
const item = document.createElement('div');
item.classList.add('item');
const itemLabel = document.createElement('div');
itemLabel.classList.add('item-label');
itemLabel.innerText = key;
const input = document.createElement('input');
input.type = 'number';
input.classList.add('item-input');
input.value = lineStyles[key];
input.max = '100';
input.oninput = (event) => {
event.preventDefault();
const value = (event.target as HTMLInputElement).value as unknown as number;
if (value < 100) {
lineStyles[key] = value;
input.value = String(value);
line.update({
style: {
stroke: [
{
color: STROKE_COLOR,
dash: [lineStyles.dash, lineStyles.gap],
width: lineStyles.width
}
]
}
});
} else {
input.value = lineStyles[key];
}
};
const suffix = document.createElement('span');
suffix.classList.add('suffix');
suffix.innerText = 'px';
item.appendChild(itemLabel);
item.appendChild(input);
item.appendChild(suffix);
container.appendChild(item);
});
return container;
}
// Method for attaching the control to the map
_onAttach() {
this._element = this._createElement();
this._detachDom = mappable.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 MMap(
// 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 MMapDefaultSchemeLayer({}),
// Add a layer of geo objects to display the line
new MMapDefaultFeaturesLayer({})
]
);
// Create a line object, set its coordinates and styles, and add it to the map
const line = new MMapFeature({
geometry: FEATURE_GEOMETRY,
style: {
simplificationRate: 0,
stroke: [{color: STROKE_COLOR, dash: [8, 8], width: 4}]
}
});
map.addChild(line);
map.addChild(
new MMapControls({position: 'top right'}, [
new MMapControl({transparent: true}).addChild(new DashStyleControl({}))
])
);
}
</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="../variables.css" />
<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>
<!-- To make the map appear, you must add your apikey -->
<script src="https://js.api.mappable.world/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">
import {FEATURE_GEOMETRY, LOCATION, STROKE_COLOR} from '../variables';
import type {DrawingStyle} from '@mappable-world/mappable-types';
import type {ChangeEvent} from 'react';
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 @mappable-world/mappable-reactify
const [mappableReact] = await Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]);
const reactify = mappableReact.reactify.bindTo(React, ReactDOM);
const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapFeature, MMapControls, MMapControl} =
reactify.module(mappable);
const {useState, useMemo, useRef, useCallback} = React;
function App() {
const [width, setWidth] = useState(4);
const [dash, setDash] = useState(8);
const [gap, setGap] = useState(8);
const style = useMemo<DrawingStyle>(
() => ({
simplificationRate: 0,
stroke: [{color: STROKE_COLOR, dash: [dash, gap], width}]
}),
[dash, gap, width]
);
const widthHandler = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const value = event.target.value as unknown as number;
if (value < 100) {
setWidth(value);
}
}, []);
const dashHandler = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const value = event.target.value as unknown as number;
if (value < 100) {
setDash(value);
}
}, []);
const gapHandler = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const value = event.target.value as unknown as number;
if (value < 100) {
setGap(value);
}
}, []);
return (
// Initialize the map and pass initialization parameters
<MMap location={LOCATION} showScaleInCopyrights={true} ref={(x) => (map = x)}>
{/* Add a map scheme layer */}
<MMapDefaultSchemeLayer />
{/* Add a layer of geo objects to display the line */}
<MMapDefaultFeaturesLayer />
{/* Add a line object to the map, set its coordinates and styles */}
<MMapFeature geometry={FEATURE_GEOMETRY} style={style} />
{/* Add a shared container for controls to the map.
Using MMapControls you can change the position of the control */}
<MMapControls position="top right">
<MMapControl transparent={true}>
<div className="container">
<div className="item">
<div className="item-label">width</div>
<input className="item-input" type="number" value={width} onChange={widthHandler} />
<span className="suffix">px</span>
</div>
<div className="item">
<div className="item-label">dash</div>
<input className="item-input" type="number" value={dash} onChange={dashHandler} />
<span className="suffix">px</span>
</div>
<div className="item">
<div className="item-label">gap</div>
<input className="item-input" type="number" value={gap} onChange={gapHandler} />
<span className="suffix">px</span>
</div>
</div>
</MMapControl>
</MMapControls>
</MMap>
);
}
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="../variables.css" />
<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 src="https://js.api.mappable.world/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="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="../variables.css" />
<link rel="stylesheet" href="./common.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
.container {
display: flex;
flex-direction: column;
align-items: flex-start;
align-self: stretch;
box-sizing: border-box;
width: 160px;
padding: 16px;
border-radius: 12px;
background: #fff;
box-shadow: 0 4px 12px 0 rgba(95, 105, 131, 0.1), 0 4px 24px 0 rgba(95, 105, 131, 0.04);
gap: 8px;
}
.item {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 32px;
gap: 10px;
}
.item-label {
font-size: 14px;
font-style: normal;
line-height: 16px;
color: var(--color-text-primary);
}
.suffix {
position: absolute;
right: 4px;
font: inherit;
user-select: none;
text-align: right;
color: #555;
}
.item-input {
box-sizing: border-box;
max-width: 60px;
height: 32px;
padding-left: 8px;
font-size: 16px;
color: var(--color-text-primary);
border: none;
border-radius: 8px;
outline: none;
background: rgba(92, 94, 102, 0.06);
transition: border-color 0.2s ease;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type='number'] {
-moz-appearance: textfield;
}
:root {
--color-text-primary: #050d33;
}
import type {LineStringGeometry, MMapLocationRequest} from '@mappable-world/mappable-types';
export const LOCATION: MMapLocationRequest = {
center: [55.3728, 25.3348], // starting position [lng, lat]
zoom: 14 // starting zoom
};
export const FEATURE_GEOMETRY: LineStringGeometry = {
type: 'LineString',
coordinates: [
[55.3609, 25.3348],
[55.3694, 25.3306],
[55.3754, 25.3394],
[55.3666, 25.3443],
[55.3609, 25.3348]
]
};
export const STROKE_COLOR = '#122DB2';