Defining Vector Layers
In AurOpenlayers, vector layers are not created manually. Instead, they are defined as declarative descriptors within a MapSchema. This approach decouples your business logic from the OpenLayers API, allowing the framework to handle the lifecycle, rendering, and synchronization of features automatically.
Understanding the Vector Layer Descriptor
A vector layer is defined using the VectorLayerDescriptor<Model, Geometry, StyleOptions> interface. It acts as a contract that describes:
- Identity: How to identify unique features.
- Geometry: How to turn your data into shapes on the map (and vice versa).
- Style: How those shapes should look.
Here is a step-by-step guide to defining a vector layer.
Step 1: Define Your Business Model
Before creating a layer, identify the data structure you want to display. For example, a simple point on a map:
interface MyPoint {
id: string;
name: string;
lat: number;
lng: number;
}
Step 2: Set the Layer Identity
Every layer requires a unique id. This ID is used later to access the Layer API via the MapContext.
const POINTS_LAYER_ID = 'points-layer';
const layerDescriptor: VectorLayerDescriptor<MyPoint, Point, any> = {
id: POINTS_LAYER_ID,
// ... configuration continues
};
Step 3: Configure the Feature Identity
The framework needs to know which feature corresponds to which object in your data array. Provide an id function that returns a unique string or number for each model.
feature: {
id: (model: MyPoint) => model.id,
// ...
}
Step 4: Map Geometry (Two-Way Synchronization)
The geometry section is the core of the descriptor. It defines how to translate your model into an OpenLayers geometry and how to update your model if the geometry changes on the map (e.g., during a drag interaction).
fromModel: Converts your business object to an OpenLayersGeometry.applyGeometryToModel: (Optional) Updates the business object when the map geometry changes. This is essential for interactive layers.
import { Point } from 'ol/geom';
import { fromLonLat, toLonLat } from 'ol/proj';
// ... inside the descriptor
geometry: {
fromModel: (model: MyPoint) =>
new Point(fromLonLat([model.lng, model.lat])),
applyGeometryToModel: (prev: MyPoint, geom: Point) => {
const [lng, lat] = toLonLat(geom.getCoordinates());
return { ...prev, lat, lng }; // Return a new immutable object
}
}
Step 5: Define Declarative Styling
AurOpenlayers uses a two-stage styling process:
base: Calculates abstract style properties (colors, sizes, labels) based on the model.render: Takes those properties and returns a native OpenLayersStyleobject.
This separation keeps your business logic (e.g., "red if urgent") away from the rendering logic (e.g., new Style(...)).
style: {
// Step 5a: Define visual properties based on data
base: (model: MyPoint) => ({
color: model.name === 'Admin' ? '#ff0000' : '#0000ff',
radius: 6,
text: model.name
}),
// Step 5b: Convert properties to OpenLayers objects
render: (opts) => new Style({
image: new CircleStyle({
radius: opts.radius,
fill: new Fill({ color: opts.color }),
stroke: new Stroke({ color: '#ffffff', width: 2 })
}),
text: new Text({
text: opts.text,
offsetY: 15
})
})
}
Step 6: Define States (Optional)
You can define specific style overrides for states like HOVER, SELECTED, or custom interaction states like DRAG.
style: {
// ... base and render
states: {
SELECTED: (model) => ({
color: '#f97316', // Override color when selected
radius: 10 // Make it larger
}),
DRAG: () => ({
color: '#22c55e' // Change color during movement
})
}
}
Full Example Implementation
In your Angular component, combine these steps into the MapHostConfig:
import { VectorLayerDescriptor } from 'aur-openlayers';
@Component({ ... })
export class MyMapComponent {
readonly mapConfig: MapHostConfig = {
schema: {
layers: [
{
id: 'delivery-points',
feature: {
id: (m: Delivery) => m.id,
geometry: {
fromModel: (m) => new Point(fromLonLat([m.lng, m.lat])),
applyGeometryToModel: (prev, geom: Point) => {
const [lng, lat] = toLonLat(geom.getCoordinates());
return { ...prev, lat, lng };
}
},
style: {
base: (m) => ({ color: m.status === 'done' ? 'green' : 'red' }),
render: (opts) => new Style({
image: new CircleStyle({
radius: 5,
fill: new Fill({ color: opts.color })
})
})
}
}
}
]
}
};
}
Tips for Success
- Immutability: Always return a new object in
applyGeometryToModel. The framework relies on immutability to detect changes efficiently. - Coordinate Systems: Remember that OpenLayers usually uses
EPSG:3857(Web Mercator) internally. UsefromLonLatandtoLonLatas shown above to work with standard[longitude, latitude]pairs. - Layer API: Once defined, you can push data to this layer using the
VectorLayerApiavailable through theonReadyevent of the map host.