Hide layers
vanilla.html
react.html
vue.html
common.css
common.ts
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="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 {generateGrid, generateMainGrid, MAP_PARAMS} from './common';
import {markersGeoJsonSource} from '../variables';
import type {MapEventUpdateHandler} from '@mappable-world/mappable-types/imperative/MMapListener';
window.map = null;
main();
async function main() {
// Waiting for all api elements to be loaded
await mappable.ready;
const {
MMap,
MMapDefaultSchemeLayer,
MMapFeatureDataSource,
MMapFeature,
MMapMarker,
MMapControls,
MMapControl,
MMapLayer,
MMapListener
} = mappable;
// Import the package to add a default marker
const {MMapDefaultMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme');
const horizontalMarkers = [];
const verticalMarkers = [];
const {features, verticalMarkersCoordinates, horizontalMarkersCoordinates} = generateGrid();
const mainGridFeatures = generateMainGrid();
const mainGridSourceLayer = new MMapLayer({
source: 'mainGridSource',
type: 'features',
zIndex: 1002
});
const degreeGridSourceLayer = new MMapLayer({
source: 'degreeGridSource',
type: 'features',
zIndex: 1001
});
const degreeGridMarkersSourceLayer = new MMapLayer({
source: 'degreeGridMarkersSource',
type: 'markers',
zIndex: 1003
});
const markerSourceLayer = new MMapLayer({
source: 'markerSource',
type: 'markers',
zIndex: 1004
});
const mapFeatures = {
features: {
title: 'degree grid',
layers: [degreeGridSourceLayer, degreeGridMarkersSourceLayer]
},
mainGridFeatures: {
title: '0 x 0',
layers: [mainGridSourceLayer]
},
markers: {
title: 'markers',
layers: [markerSourceLayer]
}
};
function createGridLabelElement(position: number) {
const markerElement = document.createElement('div');
markerElement.classList.add('grid-marker');
markerElement.innerText = `${position}°`;
return markerElement;
}
class LayerControl extends mappable.MMapComplexEntity<{}> {
private _element: HTMLDivElement;
private _detachDom: () => void;
// Method for create a DOM control element
_createElement() {
// Create a root element
const containerElement = document.createElement('div');
containerElement.classList.add('container');
const control = document.createElement('div');
control.classList.add('control');
for (const mapFeature of Object.values(mapFeatures)) {
const controlBlock = document.createElement('div');
controlBlock.classList.add('block');
// Create a title element
const title = document.createElement('div');
title.classList.add('title');
title.textContent = mapFeature.title;
controlBlock.appendChild(title);
// Create a label element
const label = document.createElement('label');
label.classList.add('label');
label.htmlFor = mapFeature.title;
// Create an input element (checkbox)
const input = document.createElement('input');
input.type = 'checkbox';
input.id = mapFeature.title;
input.checked = true;
input.onchange = (event) => {
const isChecked = (<HTMLInputElement>event.target).checked;
if (isChecked) {
mapFeature.layers.forEach((item) => map.addChild(item));
} else {
mapFeature.layers.forEach((item) => map.removeChild(item));
}
};
// Create a slider element
const slider = document.createElement('div');
slider.classList.add('slider');
// Add elements to the label
label.appendChild(input);
label.appendChild(slider);
// Add the label to the root element
controlBlock.appendChild(label);
control.appendChild(controlBlock);
}
containerElement.appendChild(control);
return containerElement;
}
// 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 = null;
this._element = null;
}
}
// Initialize the map
map = new MMap(
// Pass the link to the HTMLElement of the container
document.getElementById('app'),
// Pass the map initialization parameters
MAP_PARAMS,
[
// Add a map scheme layer
new MMapDefaultSchemeLayer({}),
new MMapFeatureDataSource({id: 'mainGridSource'}),
mainGridSourceLayer,
new MMapFeatureDataSource({id: 'degreeGridSource'}),
degreeGridSourceLayer,
new MMapFeatureDataSource({id: 'degreeGridMarkersSource'}),
degreeGridMarkersSourceLayer,
new MMapFeatureDataSource({id: 'markerSource'}),
markerSourceLayer
]
);
const bounds = map.bounds;
features.forEach((feature) => {
const mapFeature = new MMapFeature({...feature, source: 'degreeGridSource'});
map.addChild(mapFeature);
});
verticalMarkersCoordinates.forEach((position) => {
const mapMarker = new MMapMarker(
{coordinates: [position, bounds[0][1]], source: 'degreeGridMarkersSource'},
createGridLabelElement(position)
);
verticalMarkers.push(mapMarker);
map.addChild(mapMarker);
});
horizontalMarkersCoordinates.forEach((position) => {
const mapMarker = new MMapMarker(
{coordinates: [bounds[1][0], position], source: 'degreeGridMarkersSource'},
createGridLabelElement(position)
);
horizontalMarkers.push(mapMarker);
map.addChild(mapMarker);
});
mainGridFeatures.forEach((feature) => {
const mapFeature = new MMapFeature({...feature, source: 'mainGridSource'});
map.addChild(mapFeature);
});
// Create default markers and add them to the map
markersGeoJsonSource.forEach((markerSource) => {
const marker = new MMapDefaultMarker({...markerSource, source: 'markerSource'});
map.addChild(marker);
});
map.addChild(
new MMapControls({position: 'top left'}, [new MMapControl({transparent: true}).addChild(new LayerControl({}))])
);
const onUpdate: MapEventUpdateHandler = (updateObject) => {
const bounds = updateObject.location.bounds;
horizontalMarkers.forEach((marker) => {
const updatedCoordinates = [bounds[1][0], marker.coordinates[1]];
marker.update({coordinates: updatedCoordinates});
});
verticalMarkers.forEach((marker) => {
const updatedCoordinates = [marker.coordinates[0], bounds[0][1]];
marker.update({coordinates: updatedCoordinates});
});
};
map.addChild(new MMapListener({onUpdate}));
}
</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" />
<link rel="stylesheet" href="../variables.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="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="react, typescript" type="text/babel">
import {generateGrid, generateMainGrid, INITIAL_BOUNDS, MAP_PARAMS} from './common';
import {markersGeoJsonSource} from '../variables';
import type {MapEventUpdateHandler} from '@mappable-world/mappable-types/imperative/MMapListener';
import type {LngLatBounds} from '@mappable-world/mappable-types';
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,
MMapLayer,
MMapFeatureDataSource,
MMapFeature,
MMapMarker,
MMapControls,
MMapControl,
MMapListener
} = reactify.module(mappable);
// Import the package to add a default marker
const {MMapDefaultMarker} = reactify.module(
await mappable.import('@mappable-world/mappable-default-ui-theme')
);
const {useState, useCallback} = React;
const {features, verticalMarkersCoordinates, horizontalMarkersCoordinates} = generateGrid();
const mainGridFeatures = generateMainGrid();
function App() {
const [bounds, setBounds] = useState < LngLatBounds > INITIAL_BOUNDS;
const [controlState, setControlState] = useState({
degreeGrid: true,
centerGrid: true,
marker: true
});
const onUpdate: MapEventUpdateHandler = useCallback((updateObject) => {
setBounds(updateObject.location.bounds);
}, []);
return (
// Initialize the map and pass initialization parameters
<MMap
ref={(x) => {
map = x;
setBounds(x.bounds);
}}
{...MAP_PARAMS}
>
{/* Add a map scheme layer */}
<MMapDefaultSchemeLayer />
<MMapFeatureDataSource id="mainGridSource" />
{controlState['centerGrid'] && <MMapLayer zIndex={1002} source="mainGridSource" type="features" />}
{mainGridFeatures.map((feature) => (
<MMapFeature source="mainGridSource" {...feature} />
))}
<MMapFeatureDataSource id="degreeGridSource" />
{controlState['degreeGrid'] && <MMapLayer zIndex={1001} type="features" source="degreeGridSource" />}
{features.map((feature) => (
<MMapFeature source="degreeGridSource" {...feature} />
))}
<MMapFeatureDataSource id="degreeGridMarkersSource" />
{controlState['degreeGrid'] && (
<MMapLayer zIndex={1004} type="markers" source="degreeGridMarkersSource" />
)}
{/* Add default markers to the map */}
{verticalMarkersCoordinates.map((position) => (
<MMapMarker source="degreeGridMarkersSource" coordinates={[position, bounds[0][1]]}>
<div className="grid-marker">{position}°</div>
</MMapMarker>
))}
{/* Add default markers to the map */}
{horizontalMarkersCoordinates.map((position) => (
<MMapMarker source="degreeGridMarkersSource" coordinates={[bounds[1][0], position]}>
<div className="grid-marker">{position}°</div>
</MMapMarker>
))}
<MMapFeatureDataSource id="markerSource" />
{controlState['marker'] && <MMapLayer zIndex={1004} type="markers" source="markerSource" />}
{/* Add default markers to the map */}
{markersGeoJsonSource.map((markerSource) => (
<MMapDefaultMarker source="markerSource" {...markerSource} />
))}
{/* Using MMapControls you can change the position of the control */}
<MMapControls position="left top">
<MMapControl transparent>
<div className="container">
<div className="control">
<div className="block">
<div className="title">degree grid</div>
<label className="label">
<input
id="switch"
type="checkbox"
checked={controlState['degreeGrid']}
onChange={(event) =>
setControlState((prevState) => ({
...prevState,
degreeGrid: event.target.checked
}))
}
/>
<div className="slider"></div>
</label>
</div>
<div className="block">
<div className="title">0 x 0</div>
<label className="label">
<input
id="switch"
type="checkbox"
checked={controlState['centerGrid']}
onChange={(event) =>
setControlState((prevState) => ({
...prevState,
centerGrid: event.target.checked
}))
}
/>
<div className="slider"></div>
</label>
</div>
<div className="block">
<div className="title">markers</div>
<label className="label">
<input
id="switch"
type="checkbox"
checked={controlState['marker']}
onChange={(event) =>
setControlState((prevState) => ({
...prevState,
marker: event.target.checked
}))
}
/>
<div className="slider"></div>
</label>
</div>
</div>
</div>
</MMapControl>
</MMapControls>
<MMapListener onUpdate={onUpdate} />
</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="./common.css" />
<link rel="stylesheet" href="../variables.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>
<!-- 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="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" />
<link rel="stylesheet" href="../variables.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
.grid-marker {
color: #34374a;
font-size: 14px;
font-weight: 500;
text-shadow: 1px 1px 0 white, -1px 1px 0 white, 1px -1px 0 white, -1px -1px 0 white;
transform: translate(-50%, -50%);
}
.container {
box-sizing: border-box;
width: 200px;
margin-top: 12px;
background: #ffffff;
padding: 12px 16px;
border-radius: 12px;
box-shadow: 0px 4px 12px 0px #5f69831a;
}
.control {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.title {
font-size: 16px;
color: #050d33;
font-weight: 500;
}
.block {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 4px 6px 2px;
}
.label {
margin-left: auto;
height: 22px;
width: 40px;
display: inline-block;
position: relative;
}
.label input {
display: none;
}
.slider {
position: absolute;
bottom: 0;
left: 0;
right: 0;
top: 0;
cursor: pointer;
border-radius: 22px;
background-color: #ccc;
transition: 0.4s;
}
.slider:before {
content: '';
width: 16px;
height: 16px;
position: absolute;
bottom: 3px;
left: 3px;
border-radius: 50%;
background-color: #fff;
transition: 0.4s;
}
.label input:checked + .slider {
background-color: var(--interact-action);
}
.label input:checked + .slider:before {
transform: translateX(18px);
}
import type {MMapFeatureProps, MMapProps} from '@mappable-world/mappable-types';
import {LngLatBounds} from '@mappable-world/mappable-types';
mappable.ready.then(() => {
mappable.import.registerCdn('https://cdn.jsdelivr.net/npm/{package}', [
'@mappable-world/mappable-default-ui-theme@0.0'
]);
});
interface Grid {
features: MMapFeatureProps[];
horizontalMarkersCoordinates: number[];
verticalMarkersCoordinates: number[];
}
export const MAX_COORDINATES_X = 179.9;
export const MAX_COORDINATES_Y = 90;
export const INITIAL_BOUNDS: LngLatBounds = [
[-180, 90],
[180, -90]
];
const HORIZONTAL_DISTANCE = 15;
const VERTICAL_DISTANCE = 30;
export const MAP_PARAMS: MMapProps = {
zoomRange: {min: 1.4, max: 5},
zoomRounding: 'smooth',
location: {center: [0, 0], zoom: 1},
margin: [20, 20, 20, 20]
};
export function generateMainGrid(): Grid['features'] {
const style = {
stroke: [{color: '#122DB2', width: 2}],
zIndex: 101
};
return [
{
geometry: {
type: 'LineString',
coordinates: [
[-MAX_COORDINATES_X, 0],
[MAX_COORDINATES_X, 0]
]
},
style
},
{
geometry: {
type: 'LineString',
coordinates: [
[0, -MAX_COORDINATES_Y],
[0, MAX_COORDINATES_Y]
]
},
style
}
];
}
export function generateGrid(): Grid {
const features: Grid['features'] = [];
const horizontalMarkersCoordinates: number[] = [];
const verticalMarkersCoordinates: number[] = [];
const style = {
stroke: [{color: '#34374A', width: 0.5}],
zIndex: 100
};
for (let i = 0; i < 6; i++) {
const currentPosition = HORIZONTAL_DISTANCE * i;
if (i !== 0) {
features.push({
geometry: {
type: 'LineString',
coordinates: [
[-MAX_COORDINATES_X, currentPosition],
[MAX_COORDINATES_X, currentPosition]
]
},
style
});
horizontalMarkersCoordinates.push(currentPosition);
}
features.push({
geometry: {
type: 'LineString',
coordinates: [
[-MAX_COORDINATES_X, -currentPosition],
[MAX_COORDINATES_X, -currentPosition]
]
},
style
});
horizontalMarkersCoordinates.push(-currentPosition);
}
for (let i = 0; i <= 6; i += 1) {
const currentPosition = i * VERTICAL_DISTANCE;
if (i !== 0) {
features.push({
geometry: {
type: 'LineString',
coordinates: [
[currentPosition, -MAX_COORDINATES_Y],
[currentPosition, MAX_COORDINATES_Y]
]
},
style
});
verticalMarkersCoordinates.push(currentPosition);
}
features.push({
geometry: {
type: 'LineString',
coordinates: [
[-currentPosition, -MAX_COORDINATES_Y],
[-currentPosition, MAX_COORDINATES_Y]
]
},
style
});
if (currentPosition !== 180) {
verticalMarkersCoordinates.push(-currentPosition);
}
}
return {features, verticalMarkersCoordinates, horizontalMarkersCoordinates};
}
:root {
--interact-action: #122db2;
}
import type {LngLat} from '@mappable-world/mappable-types';
// Array containing GeoJSON data for markers
export const markersGeoJsonSource = [
{
coordinates: [-95.3835, 40.7873] as LngLat,
title: 'North America',
color: 'seawave',
size: 'normal',
iconName: 'fallback'
},
{
coordinates: [-53.8197, -9.3457] as LngLat,
title: 'South America',
color: 'pink',
size: 'normal',
iconName: 'fallback'
},
{
coordinates: [16.2936, 17.4555] as LngLat,
title: 'Africa',
color: 'darksalmon',
size: 'normal',
iconName: 'fallback'
},
{
coordinates: [104.9752, 44.7124] as LngLat,
color: 'green',
title: 'Asia',
size: 'normal',
iconName: 'fallback'
},
{
coordinates: [37.8337, 56.7911] as LngLat,
color: 'orchid',
title: 'Europe',
size: 'normal',
iconName: 'fallback'
},
{
coordinates: [129.07431, -23.694] as LngLat,
color: 'bluebell',
title: 'Australia',
size: 'normal',
iconName: 'fallback'
},
{
coordinates: [52.874, -69.178] as LngLat,
color: 'steelblue',
title: 'Antarctica',
size: 'normal',
iconName: 'fallback'
}
];