Skip to main content

Shapes

Drawing lines

Draw routes and paths using GeoJSON LineString.

import { Camera, LineLayer, MapView, MarkerView, ShapeSource } from '@maplibre/maplibre-react-native';
import type { FeatureCollection } from 'geojson';
import React from 'react';
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
import { BARIKOI_COLORS, MAP_STYLES, useBarikoiMapStyle } from '../utils/mapUtils';

export default function LineScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();

const lineGeoJSON: FeatureCollection = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[90.364159, 23.823724],
[90.369159, 23.825724],
],
},
}],
};

if (loading) return <View style={styles.centered}><ActivityIndicator size="large" color={BARIKOI_COLORS.primary} /></View>;
if (error) return <View style={styles.centered}><Text>{error}</Text></View>;

return (
<View style={styles.container}>
<MapView style={styles.map} attributionEnabled={false} zoomEnabled compassEnabled mapStyle={styleJson}>
<Camera centerCoordinate={[90.366659, 23.824724]} zoomLevel={15} animationMode="linearTo" />
<ShapeSource id="lineSource" shape={lineGeoJSON}>
<LineLayer id="lineLayer" style={MAP_STYLES.line} />
</ShapeSource>
<MarkerView coordinate={[90.364159, 23.823724]} anchor={{ x: 0.5, y: 1.0 }}>
<View style={styles.dot} />
</MarkerView>
<MarkerView coordinate={[90.369159, 23.825724]} anchor={{ x: 0.5, y: 1.0 }}>
<View style={styles.dot} />
</MarkerView>
</MapView>
</View>
);
}

const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
dot: { width: 14, height: 14, borderRadius: 7, backgroundColor: BARIKOI_COLORS.primary },
});

Line style properties:

PropertyTypeDescription
lineColorstringColor (e.g. '#2e8555')
lineWidthnumberWidth in pixels
lineCap"butt" | "round" | "square"End cap style
lineJoin"bevel" | "round" | "miter"Join style
lineDasharraynumber[]e.g. [2, 2] for dashed lines

Drawing polygons

Render filled areas using GeoJSON Polygon.

Close the ring

The first and last coordinate of a polygon ring must be identical. If they don't match, the polygon will not render.

import { Camera, FillLayer, LineLayer, MapView, MarkerView, ShapeSource } from '@maplibre/maplibre-react-native';
import React from 'react';
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
import { BARIKOI_COLORS, MAP_STYLES, useBarikoiMapStyle } from '../utils/mapUtils';

export default function PolygonScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();

const polygonGeoJSON = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [[
[90.364159, 23.823724],
[90.369159, 23.825724],
[90.367159, 23.820724],
[90.364159, 23.823724], // must equal first point
]],
},
}],
};

const vertices = [[90.364159, 23.823724], [90.369159, 23.825724], [90.367159, 23.820724]];

if (loading) return <View style={styles.centered}><ActivityIndicator size="large" color={BARIKOI_COLORS.primary} /></View>;
if (error) return <View style={styles.centered}><Text>{error}</Text></View>;

return (
<View style={styles.container}>
<MapView style={styles.map} attributionEnabled={false} zoomEnabled compassEnabled mapStyle={styleJson}>
<Camera centerCoordinate={[90.366659, 23.823724]} zoomLevel={15} animationMode="linearTo" />
<ShapeSource id="polygonSource" shape={polygonGeoJSON}>
<FillLayer id="polygonFill" style={MAP_STYLES.polygon} />
{/* Add a LineLayer for a thicker outline */}
<LineLayer id="polygonOutline" style={{ lineColor: '#2e8555', lineWidth: 3 }} />
</ShapeSource>
{vertices.map((coord, i) => (
<MarkerView key={i} coordinate={coord} anchor={{ x: 0.5, y: 1.0 }}>
<View style={styles.dot} />
</MarkerView>
))}
</MapView>
</View>
);
}

const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
dot: { width: 14, height: 14, borderRadius: 7, backgroundColor: BARIKOI_COLORS.primary },
});

Polygon fill properties:

PropertyTypeDescription
fillColorstringFill color (e.g. 'rgba(46, 133, 85, 0.5)')
fillOutlineColorstringBorder color
fillOpacitynumberOpacity 0–1

For a thicker outline, add a LineLayer after the FillLayer inside the same ShapeSource.


Circle polygons

MapLibre does not have a built-in circle shape. Use the createCirclePolygon utility from mapUtils.ts to approximate one with a many-sided polygon.

import { createCirclePolygon } from '../utils/mapUtils';

const circleCoords = createCirclePolygon([90.364159, 23.823724], 0.5); // 500m radius

const circleGeoJSON = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [circleCoords], // wrap in array for the ring
},
}],
};

// Render with FillLayer
<ShapeSource id="circleSource" shape={circleGeoJSON}>
<FillLayer id="circleFill" style={MAP_STYLES.polygon} />
</ShapeSource>

createCirclePolygon parameters:

ParameterTypeDescription
center[number, number][longitude, latitude]
radiusInKmnumberRadius in kilometers (e.g. 0.5 for 500m)
pointsnumberVertex count, default 64 (higher = smoother)

Combined geometry

Combine circles, lines, polygons, and markers on a single map. Each shape type uses its own ShapeSource and layer.

Unique IDs required

Every ShapeSource and layer must have a unique id across the entire map. Duplicate IDs will cause rendering errors.

import { Camera, FillLayer, LineLayer, MapView, MarkerView, ShapeSource } from '@maplibre/maplibre-react-native';
import type { FeatureCollection } from 'geojson';
import React from 'react';
import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
import { BARIKOI_COLORS, MAP_STYLES, createCirclePolygon, useBarikoiMapStyle } from '../utils/mapUtils';

export default function GeometryScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();
const center: [number, number] = [90.366659, 23.823724];

const circleGeoJSON: FeatureCollection = {
type: 'FeatureCollection',
features: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [createCirclePolygon(center, 0.3)] } }],
};

const lineGeoJSON: FeatureCollection = {
type: 'FeatureCollection',
features: [{ type: 'Feature', properties: {}, geometry: { type: 'LineString', coordinates: [[90.364159, 23.823724], [90.369159, 23.825724]] } }],
};

const markerPoints = [[90.364159, 23.823724], [90.369159, 23.825724], center];

if (loading) return <View style={styles.centered}><ActivityIndicator size="large" color={BARIKOI_COLORS.primary} /></View>;
if (error) return <View style={styles.centered}><Text>{error}</Text></View>;

return (
<View style={styles.container}>
<MapView style={styles.map} attributionEnabled={false} zoomEnabled compassEnabled mapStyle={styleJson}>
<Camera centerCoordinate={center} zoomLevel={15} animationMode="linearTo" />
<ShapeSource id="circleSource" shape={circleGeoJSON}>
<FillLayer id="circleFill" style={MAP_STYLES.polygon} />
</ShapeSource>
<ShapeSource id="lineSource" shape={lineGeoJSON}>
<LineLayer id="lineLayer" style={MAP_STYLES.line} />
</ShapeSource>
{markerPoints.map((coord, i) => (
<MarkerView key={i} coordinate={coord} anchor={{ x: 0.5, y: 1.0 }}>
<View style={styles.dot} />
</MarkerView>
))}
</MapView>
</View>
);
}

const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
dot: { width: 14, height: 14, borderRadius: 7, backgroundColor: BARIKOI_COLORS.primary },
});