Skip to main content
Version: 6.0

Labels

Mappedin SDK for React Native version 6 is currently in an alpha state while Mappedin perfects new features and APIs. Open the v6 release notes to view the latest changes.
Using Mappedin JS with your own map requires a Pro license. Try a demo map for free or refer to the Pricing page for more information.

Labels display text and images anchored to specific points on a map. They rotate, show, or hide based on priority and zoom level, providing information about location names, points of interest, and more. Effective labels help apps convey additional information, such as room names, points of interest, main entrances, or other useful contextual details.

Note that the MapViewControl class implements the Mappedin.Labels interface and exposes it as MapViewControl.Labels. Use MapViewControl.Labels to utilize Labels' methods. An instance of MapViewControl is available from the useMap hook.

Mappedin JS v6 Labels

Displaying All Labels

To display all labels that have been added to the source map, use the Mappedin.Labels.all(). The following code sample demonstrates use of the all() method:

// Get all spaces with names
const spaces = mapData.getByType('space').filter(space => space.name);

// Add labels for all spaces
return (
<>
{spaces.map(space => (
<Label
key={space.externalId}
target={space}
text={space.name}
/>
))}
</>
);

The opposite of the all() method is the Mappedin.Labels.removeAll() method, which removes all labels from the map.

// Remove all the labels from the map.
mapView.Labels.removeAll();

Adding & Removing Individual Labels

Labels can be added individually to a map by calling Mappedin.Labels.add(). A label can be added to any Mappedin.IAnchorable target. The following code sample adds a label to each space that has a name:

// Get all spaces with names
const spaces = mapData.getByType('space').filter(space => space.name);

// Label all spaces with its space name and make them interactive.
return (
<>
{spaces.map(space => (
<Label
key={space.externalId}
target={space}
text={space.name} // label text
options={{ interactive: true }} // makes the label interactive
/>
))}
</>
);

Labels can be removed by using the Mappedin. Labels.remove(label) method, passing in the label to be removed as shown below:

// Remove a label.
mapView.Labels.remove(label);

Interactive Labels

Labels added to the map can be set to interactive to allow users to click on them. For more information, refer to the Interactive Labels section of the Interactivity Guide.

Label Icons

Icons can be added to labels in SVG, PNG, JPEG and WebP formats. Icons are clipped in a circle to prevent overflow. Three clipping methods of contain, fill and cover can be set in the Mappedin.TLabelAppearance.iconFit parameter with contain being the default.

FillContainCover (default)
Floating Label fillFloating Label containFloating Label cover

The Mappedin.TLabelAppearance.iconPadding property sets the amount of space between the icon and the border. The icon may shrink based on this value.

padding: 0padding: 10
Floating Label fill with 0 paddingFloating Label fill with 10 padding

Label icons can be configured to be shown or hidden based on the current zoom level using Mappedin.TLabelAppearance.iconVisibilityThreshold. A value below 0 will result in the icon always being hidden. Setting this value to 0 ensures icons show up at maxZoom (fully zoomed in) and 1 configures them to always be displayed.

Label Appearance

Labels can have their appearance styled to match the visual theme of an app or to make groups of labels easily distinguishable from one another. The following type declaration of Mappedin.TLabelAppearance describes these customisable attributes.

interface TLabelAppearance {
margin?: number;
marker?: {
backgroundColor?: { active?: string; inactive?: string };
foregroundColor?: { active?: string; inactive?: string };
icon?: string;
iconFit?: "fill" | "contain" | "cover";
iconOverflow?: "visible" | "hidden";
iconPadding?: number;
iconScale?: number | Interpolation<"zoom-level", number[]>;
iconSize?: number;
iconVisibleAtZoomLevel?: number;
};
text?: {
backgroundColor?: string;
foregroundColor?: string;
lineHeight?: number;
maxWidth?: number;
numLines?: number;
size?: number;
};
}

Mappedin.TLabelAppearance

Margin:

  • margin in pixels around the label and marker. This will affect label density.

Marker styles:

  • backgroundColor & foregroundColor The marker takes both active and inactive variants of its foreground and background colors.
  • icon is the SVG of icon to place inside a label.
  • iconSize is the size of the bounding box of the SVG icon.
  • iconVisibilityThreshold defines when the icon becomes visible relative to the current zoom level. Values should be between 0 and 1. 0 appears at maximum zoom and 1 causes the marker to always be shown.

Text styles:

  • foregroundColor and backgroundColor colors that can be set using CSS colors, as Hex strings or the CSS name of a color such as crimson.
  • lineHeight sets the height of a text line box. The default is 1.2.
  • maxWidth defines the maximum width of text in pixels.
  • numLines sets the maximum number of lines to display if the text spans multiple lines.
  • size is the text size in pixels.

The code sample below demonstrates how to use some label styling options. It specifies an SVG icon to use and sets the icon and text color. Labels are added to each space with a name.

<Label
key="sirens-label"
target={sirenSpace}
text={`🎵 Sirens`}
options={{
interactive: true,
rank: "high",
appearance: {
text: {
foregroundColor: "#E91E63",
},
marker: {
foregroundColor: {
active: "#4CAF50",
inactive: "#999",
},
icon: "https://your-domain.com/icons/your-icon.svg",
},
},
}}
onLoad={(label) => {
console.log("Sirens label loaded:", label);
}}
/>

Label Ranking

Ranking can be added to labels to control which label will be shown when more than one label occupies the same space. The label with the highest rank will be shown. If labels do not overlap, ranking will have no effect. Rank values are low, medium, high, and always-visible and are defined in Mappedin.TCollisionRankingTier.

The code below adds labels where a user clicks on the map. The label rank is cycled with each click. If the user clicks and adds multiple labels in the same location, the label with the highest rank is shown and lower ranking labels are hidden.

const [labels, setLabels] = useState<
{
coordinate: Mappedin.Coordinate;
text: string;
rank: Mappedin.TCollisionRankingTier;
}[]
>([]);

const RANKS: Mappedin.TCollisionRankingTier[] = ['low', 'medium', 'high', 'always-visible'];
const [currentRank, setCurrentRank] = useState(0);

// Handle map clicks to add a label with a cycling rank
useEvent('click', event => {
const newRank = (currentRank + 1) % RANKS.length; // Cycle through ranks
setCurrentRank(newRank);

const newLabel = {
coordinate: event.coordinate,
text: `Label Rank ${RANKS[newRank]}`,
rank: RANKS[newRank],
};

setLabels(prevLabels => [...prevLabels, newLabel]);
});

return (
<>
{labels.map((label, index) => (
<Label
key={index} // Use index as a fallback key for simplicity
target={label.coordinate} // Add the label to the clicked coordinate
text={label.text}
options={{
rank: label.rank, // Set the rank for the label
}}
/>
))}
</>
);

Enabling and Disabling Labels

Labels can be dynamically enabled or disabled using MapViewControl.updateState() or the Mappedin.TAddLabelOptions.enabled. When a label is disabled, it will be hidden from view but remain in the map's memory. This is useful for managing label visibility based on conditions like zoom level or user interaction, without the overhead of repeatedly adding and removing labels.

Use MapViewControl.getState() to check a label's current state, which returns the label's current properties including its enabled status.

Here's an example on how to enable/disable labels on click:

const [isEnabled, setIsEnabled] = useState(false);

return (
<Label
target={space}
text="Dynamic Label"
options={{
enabled: isEnabled // label will be hidden when false
}}
/>
);

A common use case is showing different sets of labels based on zoom level:

const [secondaryLabels, setSecondaryLabels] = useState(
spaces.slice(5).map(space => ({
target: space,
text: space.name,
enabled: false
}))
);

useEvent('camera-change', (transform) => {
setSecondaryLabels(labels =>
labels.map(label => ({
...label,
enabled: transform.zoomLevel >= 20
}))
);
});

return (
<>
{/* primary labels - always visible */}
{spaces.slice(0, 5).map(space => (
<Label
key={space.id}
target={space}
text={space.name}
options={{ enabled: true }}
/>
))}

{/* secondary labels - conditionally visible */}
{secondaryLabels.map(label => (
<Label
key={label.target.id}
target={label.target}
text={label.text}
options={{ enabled: label.enabled }}
/>
))}
</>
);