Skip to main content

MVF v1: Render with Leaflet

As an extension of GeoJSON, the Mappedin Venue Format (MVF) is easy to integrate with various GIS tools and technologies. We use Typescript in this guide to help us with the GeoJSON data structure but the code works with small modifications in Javascript as well.

In this guide, we will render the Mappedin Demo Mall using a popular web map library, Leaflet.

GeoJSON Exports - Render Leaflet 1

Setup

  1. Start a new Vite Typescript -project (or JS tooling of your choice) with
    • yarn create vite mappedin-leaflet
    • Select Vanilla and Vanilla-ts
    • Then run cd mappedin-leaflet && yarn
    • yarn dev to start run the project. This will display a Vite welcome page
  2. Download the MVF sample package
  3. Extract the contents of Demo Mall MVF folder to to data/mappedin-demo-mall/ in the project folder
  4. Add Leaflet with yarn add leaflet
  5. Install Leaflet and GeoJSON types as DevDependencies yarn add -D @types/leaflet @types/geojson

Modify your index.html to the following

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Leaflet MVF</title>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin=""
/>
<style>
body,
html {
margin: 0;
padding: 0;
}
#map {
height: 100vh;
}
</style>
</head>
<body>
<div id="map"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

Clear src/main.ts and add the following imports and styles. For spaces, we use a StyleFunction that returns PathOptions based on the GeoJSON feature's properties.

import * as L from 'leaflet';
import * as GeoJSON from 'geojson';

const styles = {
building: {
color: '#BF4320',
opacity: 0.5,
fillOpacity: 1,
fillColor: '#fafafa',
dashArray: '5 10',
},
level: {
fillColor: '#777',
fillOpacity: 1,
color: 'black',
weight: 1,
},
spaces: (feature: any) => {
return {
color: 'black',
weight: 1,
opacity: 1,
fillOpacity: 1,
fillColor: feature.properties!.color || 'white',
};
},
obstruction: {
weight: 0,
fillColor: 'black',
fillOpacity: 1,
},
node: {
radius: 2,
fillColor: '#BF4320',
color: '#000',
weight: 1,
opacity: 1,
fillOpacity: 0.4,
},
};

Loading MVF data

We will load the data using fetch. Manifest.geojson-file in the MVF folder explains the structure and other files in the bundle. Data model documentation explains the files and their properties in detail.

async function loadData(filename: string) {
return (await fetch(`${filename}`)).json();
}

Mappedin Demo Mall features 3 levels and most of the data is divided into 3 files named after the level id. For this quick guide, we will hardcode our level id. . We can find the level id of "Lower Level" by looking at the properties of the level -files and selecting the correct file name.

const levelId = '5835ab589321170c11000000';

const building = (await loadData('data/mappedin-demo-mall/building.geojson')) as GeoJSON.FeatureCollection;

const level = (await loadData(`data/mappedin-demo-mall/level/${levelId}.geojson`)) as GeoJSON.FeatureCollection;

const space = (await loadData(`data/mappedin-demo-mall/space/${levelId}.geojson`)) as GeoJSON.FeatureCollection;

const obstruction = (await loadData(
`data/mappedin-demo-mall/obstruction/${levelId}.geojson`,
)) as GeoJSON.FeatureCollection;

Creating and drawing layers

We create the building layer first so that we can use it to define the area where Leaflet displays the map. getBounds() calculates the boundaries needed to display the whole area described by the building polygon data. The map is drawn to the element with id map.

const buildingLayer = L.geoJSON(building, {
style: styles.building,
});

const map = L.map('map', {
maxBounds: buildingLayer.getBounds(),
});

map.fitBounds(buildingLayer.getBounds());

With the spaces, we filter the data to make sure that we only draw polygons, because it also includes Point geometry.

const levelLayer = L.geoJSON(level, { style: styles.level });
const obstructionLayer = L.geoJSON(obstruction, { style: styles.obstruction });

const spaceLayer = L.geoJSON(space, {
style: styles.spaces,
filter: (feature) => {
return feature.geometry.type === 'Polygon';
},
});

Add layers to the map in the order in which they are drawn

buildingLayer.addTo(map);
levelLayer.addTo(map);
obstructionLayer.addTo(map);
spaceLayer.addTo(map);

This is how the map looks at the end. The orange dots represent nodes, which can be used for pathfinding through the venue. The MVF package comes with rich data about each location in the venue and all of that can be added on top of the base built in this tutorial.