Plugin Map
Map visualization component for ObjectQL data sources - displays database records as map markers based on location data.
Installation
npm install @object-ui/plugin-mapOverview
The @object-ui/plugin-map plugin provides map visualization for ObjectQL data sources. It's designed to work with object-based data providers and automatically maps record fields to map markers/pins.
Note: This is a basic implementation suitable for simple use cases. For production applications with advanced mapping needs, consider integrating a dedicated mapping library like Mapbox, Leaflet, or Google Maps.
Features
- ObjectQL Integration: Works seamlessly with object/value data providers
- Automatic Field Mapping: Maps database fields to map markers
- Location Support: Handle latitude/longitude or combined location fields
- Marker Customization: Configurable marker titles and descriptions
- Marker Clustering: Group nearby markers (when many points)
- Popup/Tooltip: Show details on marker click
- Interactive: Click handling for markers
Usage
Basic Usage with ObjectQL
import '@object-ui/plugin-map'
const schema = {
type: 'object-map',
objectName: 'locations', // Your ObjectQL object
map: {
latitudeField: 'lat',
longitudeField: 'lng',
titleField: 'name',
descriptionField: 'address'
}
}With Static Data
const schema = {
type: 'object-map',
staticData: [
{
id: 1,
name: 'Office HQ',
lat: 37.7749,
lng: -122.4194,
address: '123 Main St, San Francisco, CA'
},
{
id: 2,
name: 'Warehouse',
lat: 37.8044,
lng: -122.2711,
address: '456 Oak Ave, Oakland, CA'
}
],
map: {
latitudeField: 'lat',
longitudeField: 'lng',
titleField: 'name',
descriptionField: 'address'
}
}Schema API
{
type: 'object-map',
objectName: string, // ObjectQL object name
staticData?: Array<any>, // Static data array
data?: ViewData, // Advanced data configuration
filter?: Array<any>, // Query filter, sent as $filter
sort?: string | SortConfig[], // Sort, sent as $orderby
map?: ObjectMapConfig, // Map-specific configuration
enableClustering?: boolean, // Cluster nearby markers (auto past 100)
navigation?: NavigationConfig, // Record navigation (drawer/dialog/page)
mapStyle?: string, // MapLibre style URL/spec
onMarkerClick?: (record: any) => void,
className?: string
}ObjectMapConfig
{
latitudeField?: string, // Field containing latitude
longitudeField?: string, // Field containing longitude
locationField?: string, // Field with combined location (alternative)
titleField?: string, // Field to use as marker title
descriptionField?: string, // Field for marker description
zoom?: number, // Zoom level (1-20) — opts out of the auto-fit
center?: [number, number], // Center coordinates [lat, lng] — opts out of the auto-fit
style?: string // MapLibre style URL/spec (overrides the demo default)
}Configuration
Field Mapping - Separate Coordinates
When your data has separate latitude and longitude fields:
{
type: 'object-map',
objectName: 'stores',
map: {
latitudeField: 'latitude',
longitudeField: 'longitude',
titleField: 'storeName',
descriptionField: 'storeAddress'
}
}Field Mapping - Combined Location
When your data has a combined location field:
{
type: 'object-map',
objectName: 'places',
map: {
locationField: 'coordinates', // e.g., "37.7749,-122.4194" or {lat: 37.7749, lng: -122.4194}
titleField: 'placeName',
descriptionField: 'description'
}
}Initial Camera
By default the map has no fixed camera: on load it fits the records it queried. The marker set's bounding box is measured along the shortest arc that contains every marker — so a set straddling the antimeridian is framed across the line rather than around the far side of the planet — and the map fits that box with padding, up to a city-scale zoom ceiling (a single record does not become a rooftop view). A view with data therefore never opens on an empty viewport.
Two cases sit outside the fit:
- No records (empty result, or every record missing coordinates): nothing to fit, so the map opens on the whole world.
- A declared camera (below): the declaration wins and the fit is skipped.
Zoom and Center
Declare either one to take the camera over and opt this view out of the auto-fit:
{
type: 'object-map',
objectName: 'locations',
map: {
latitudeField: 'lat',
longitudeField: 'lng',
titleField: 'name',
zoom: 12, // Zoom level (1-20)
center: [37.7749, -122.4194] // [latitude, longitude]
}
}Declaring only one half keeps the other derived: zoom on its own is applied at
the centre of the records, center on its own at a continental zoom.
Data Providers
Object Provider (Database)
{
type: 'object-map',
objectName: 'retail_stores',
map: {
latitudeField: 'store_lat',
longitudeField: 'store_lng',
titleField: 'store_name',
descriptionField: 'store_address'
}
}Value Provider (Static)
{
type: 'object-map',
staticData: [
{ id: 1, name: 'Location 1', lat: 37.7749, lng: -122.4194 },
{ id: 2, name: 'Location 2', lat: 37.8044, lng: -122.2711 }
],
map: {
latitudeField: 'lat',
longitudeField: 'lng',
titleField: 'name'
}
}API Provider — not implemented
data.provider: 'api' has no fetch implementation in ObjectMap. A schema that
reaches this branch logs API provider not yet implemented for ObjectMap, sets
the record set to empty and renders a map with no markers; endpoint and
method have no read point anywhere in the package. Without a DataSource it
fails one step earlier, with DataSource required for object/api providers.
Read from the database with the Object Provider above, or pass records you already hold with the Value Provider.
Event Handling
Marker Click
{
type: 'object-map',
objectName: 'locations',
map: {
latitudeField: 'lat',
longitudeField: 'lng',
titleField: 'name'
},
onMarkerClick: (location) => {
console.log('Marker clicked:', location);
// Show location details
// Navigate to location page
// Open directions
}
}Examples
Store Locator
const storeLocator = {
type: 'object-map',
objectName: 'retail_locations',
map: {
latitudeField: 'latitude',
longitudeField: 'longitude',
titleField: 'storeName',
descriptionField: 'fullAddress',
zoom: 10,
center: [37.7749, -122.4194] // San Francisco
},
onMarkerClick: (store) => {
// Show store details
// Display hours, phone, etc.
}
}Delivery Tracking
const deliveryMap = {
type: 'object-map',
objectName: 'active_deliveries',
map: {
latitudeField: 'current_lat',
longitudeField: 'current_lng',
titleField: 'driver_name',
descriptionField: 'delivery_address'
},
onMarkerClick: (delivery) => {
// Show delivery details
// Contact driver
}
}Real Estate Listings
const propertyMap = {
type: 'object-map',
objectName: 'properties',
map: {
latitudeField: 'property_lat',
longitudeField: 'property_lng',
titleField: 'property_address',
descriptionField: 'property_details',
zoom: 12
},
onMarkerClick: (property) => {
// Show property details
// Display photos, price, etc.
}
}Event Venues
const venueMap = {
type: 'object-map',
staticData: [
{
id: 1,
venueName: 'Conference Center',
lat: 37.7833,
lng: -122.4167,
details: 'Capacity: 500 people'
},
{
id: 2,
venueName: 'Exhibition Hall',
lat: 37.7891,
lng: -122.3894,
details: 'Capacity: 1000 people'
},
{
id: 3,
venueName: 'Outdoor Amphitheater',
lat: 37.7694,
lng: -122.4862,
details: 'Capacity: 2000 people'
}
],
map: {
latitudeField: 'lat',
longitudeField: 'lng',
titleField: 'venueName',
descriptionField: 'details',
zoom: 11
},
onMarkerClick: (venue) => {
// Show venue details
// Book venue
}
}Field Service Map
const serviceMap = {
type: 'object-map',
objectName: 'service_calls',
map: {
latitudeField: 'customer_latitude',
longitudeField: 'customer_longitude',
titleField: 'customer_name',
descriptionField: 'service_type',
zoom: 10
},
onMarkerClick: (serviceCall) => {
// Show service call details
// Assign technician
// Get directions
}
}Typical Use Cases
- Store Locator: Display retail store locations
- Fleet Tracking: Show vehicle or delivery locations
- Real Estate: Display property listings on a map
- Event Venues: Show event or venue locations
- Field Service: Track service calls or technician locations
- Customer Locations: Visualize customer distribution
- Asset Tracking: Show location of equipment or assets
Location Data Formats
Separate Fields
{
id: 1,
name: 'Location',
latitude: 37.7749,
longitude: -122.4194
}Combined String
{
id: 1,
name: 'Location',
coordinates: '37.7749,-122.4194'
}Object Format
{
id: 1,
name: 'Location',
location: {
lat: 37.7749,
lng: -122.4194
}
}Map Controls
The map typically includes:
- Zoom controls: Zoom in/out buttons
- Pan: Drag to move around the map
- Marker click: Click markers to show details
- Auto-fit: Automatically adjust view to show all markers
Integration with Full-featured Map Libraries
For production applications, you may want to integrate with:
- Mapbox: Advanced styling, 3D maps, routing
- Google Maps: Street view, directions, extensive POI data
- Leaflet: Open-source, lightweight, customizable
- OpenStreetMap: Free, community-driven map data
This plugin provides a basic implementation. For advanced features like:
- Directions/routing
- Street view
- 3D buildings
- Traffic data
- Custom map styles
- Advanced geocoding
Consider using one of the full-featured mapping libraries above.
TypeScript Support
import type { ObjectMapSchema, ObjectMapConfig } from '@object-ui/types'
const mapConfig: ObjectMapConfig = {
latitudeField: 'lat',
longitudeField: 'lng',
titleField: 'name',
descriptionField: 'description',
zoom: 12,
center: [37.7749, -122.4194]
}
const mapSchema: ObjectMapSchema = {
type: 'object-map',
objectName: 'locations',
map: mapConfig
}