Skip to main content

MVF Overview

The Mappedin Venue Format (MVF) is a GeoJSON based format to describe indoor maps and their surroundings. It has a simple, flexible core of GeoJSON broken up by floor, with a powerful extension framework to add many different kinds of data.

TypeScript/JavaScript developers will primarily interact with MVFs though the main MVF package. It contains the parser to unzip an MVF bundle and convert it into a typed JavaScript object of the MVFv3 type. It also re-exports the Mappedin extensions, so developers only need to install the @mappedin/mvf extension for most common cases.

Developers looking at the MVF directly can find specifics on each extension in their specific docs. The most common ones are:

  • Core - The main extension almost every MVF will implement. Includes the manifest, floors, and geometry (one file per floor).
  • Locations - Rich data for places of interest, linked to geometry.

Others may be used, but many, such as Connections, Nodes, and Navigation Flags, are mostly used by the Mappedin SDK.

Core

Almost every MVF will contain the Core extension. In particular, most extensions will contain references to Geometry and Floors.

Here is a very simple example, as an MVFv3 object:

import type { MVFv3 } from '@mappedin/mvf';

export const CORE_EXAMPLE: MVFv3 = {
manifest: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [0, 0],
},
properties: {
name: 'My Map',
version: '3.0.0',
time: '2025-01-01T00:00:00.000Z',
contents: [
{
name: 'manifest.geojson',
type: 'file',
},
{
name: 'floors.geojson',
type: 'file',
},
{
name: 'geometry',
type: 'folder',
children: [
{
name: 'f_00000001.geojson',
type: 'file',
},
{
name: 'f_00000002.geojson',
type: 'file',
},
],
},
],
},
},
],
},
floors: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0],
],
],
},
properties: {
id: 'f_00000001',
elevation: 0,
details: {
name: 'Floor 1',
},
},
},
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0],
],
],
},
properties: {
id: 'f_00000002',
elevation: 1,
details: {
name: 'Floor 2',
},
},
},
],
},
geometry: {
f_00000001: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0],
],
],
},
properties: {
id: 'g_00000001',
},
},
],
},
f_00000002: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [
[
[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0],
],
],
},
properties: {
id: 'g_00000002',
},
},
],
},
},
};

That would have come from a zip file with the following structure:

manifest.geojson
floors.geojson
geometry/
f_00000001.geojson
f_00000002.geojson

This describes a map, called "My Map", with two floors. Each has one geometry feature, which is a polygon. All of the files exposed by Core are GeoJSON feature collections. The manifest has a single point that is roughly the center of the map as a whole. Floors contains a outline of each floor, and geometry contains all of the actual geometry (walls, rooms, doors, desks, etc.) for each floor. There may be a few extra properties on these objects (see their types for details), but most other, more complex relationships, will be described in other extensions.

"Core" Concepts

There are a few concepts and patterns that Core provides that are important to understand.

Files vs Objects

MVF is a bundle format, but most developers will interact with it as a parsed JavaScript object. This means that when describing the structure of an MVF, this documentation will tend to prefer the object model over the file model. As can be seen from the example above, an MVF bundle is parsed into a single object, with top level properties for every file and folder in the root of the zip. For any file, they will be parsed as JSON and their extension removed. For any folder, their children (files and folders) will be parsed the same way and added to the object as a child property.

So in the example above, the contents of the "manifest.geojson" file is what is now in the manifest property of the MVFv3 object. The contents of the "floors.geojson" file is what is now in the floors property of the MVFv3 object. The contents of the "geometry" folder would be two files, one for each floor, with the name of the file being the floor id + ".geojson", and the contents of the file being what is now in the geometry property of the MVFv3 object, on the keys of the floor ids.

One File Per Floor

If at all possible, an extension will provide a top level property for itself, an object per floor, keyed by the floor id. Geometry in Core is a good example of this, but many extensions will do this as well. Floors are a fundamental concept that differential MVF from other geo-spatial formats, in particular outdoor oriented ones. Each floor can contain a lot of data, and a client may wish to only parse or consider data on a certain floor in many situations.

Utility Types

Core exports a number of utility types that are used by many extensions to have a common way of handling certain ideas.

WithGeometryId and WithGeometryAnchor

Nearly all extensions will involve relating some information to some Geometry from the Core extension. The Geometry object is kept deliberately minimal, with the idea being that one or more extensions will have references to geometries to add more data and context.

For example, for a given piece of Geometry, a Location such as a Restroom may reference it, along with several other geometries, to indicate where the restrooms are. A node may reference it to say a user can reach this place through the node graph by getting to that node. The Default Style extension may reference the geometry to say it should rendered blue.

All of this is done in a standard way, though two main utility types, exported from Core: WithGeometryId and WithGeometryAnchor, and their array variants (WithGeometryIds and WithGeometryAnchors).

WithGeometryId adds a geometryId property to a type, and WithGeometryIds adds an array of geometryIds properties.

export type WithGeometryId = {
geometryId: GeometryId;
}

export type WithGeometryIds = {
geometryIds: GeometryId[];
}

WithGeometryAnchor adds a geometryAnchor property to a type, containing a GeometryAnchor, and WithGeometryAnchors adds geometryAnchors, which is an array of GeometryAnchors. A GeometryAnchor is an object with a geometryId and floorId.

export type GeometryAnchor = {
geometryId: GeometryId;
floorId: FloorId;
}

export type WithGeometryAnchor = {
geometryAnchor: GeometryAnchor;
}

export type WithGeometryAnchors = {
geometryAnchors: GeometryAnchor[];
}

An extension will use withGeometryId if it is already known what floor it belongs to (ie, if it is following One File Per Floor practice and is inside of a object keyed by the floor id). If it is not known what floor it belongs to, it will use withGeometryAnchor instead.

WithDetails

Core also exports a WithDetails utility type, which is used to add a standard details property to an object. This is the way to add a small amount of additional (typically optional) user facing metadata to an object.

Extensions will tend to add a details object rather than any of the properties inside of it directly. Developers can build default components to display information about objects that are WithDetails if they don't have a more specific display.

export type Details = {
description?: string;
externalId?: string;
icon?: string;
name?: string;
shortName?: string;
};

export type WithDetails = {
details?: Details;
}

externalId is worth noting specifically. This is a string that should be used to link the object to some other system external to Mappedin. For example, the core Geometry extension marks Geometry as withDetails and a particular geometry might get an externalId of UNIT-21 to represent the unit number in some leasing system.

Extra

Core also includes a WithExtras utility type, which is used to add the extra property to an object, which is a grab back of string keys to any data. This should be used sparingly, preferring instead a more specific extension, since there is no validation or integrity checking on the data. Enterprise customers with custom data syncs and custom SDK applications may have some of their own data included in the extra property on certain objects, as per their agreements with Mappedin.

export type Extra = Record<string, unknown>;

export type WithExtras = {
extra?: Extra;
}

Example

The Locations extension uses all three of these utility types. Here is a simple example of a Location object:

const location: Location = {
id: 'loc_00000001',
geometryAnchors: [
{
geometryId: 'g_00000001',
floorId: 'f_00000001',
},
],
details: {
name: 'Main Conference Room',
},
extra: {
customData: 'some data',
},

categories: [],
social: [],
images: [],
links: [],
};

This location can be found on floor one. It is the Main Conference Room, and it has some custom data.