The Store is the heart of the library and is responsible for managing the state of all Features that are added to the map. The Store is created when Terra Draw is instantiated, but is not directly exposed, and instead exposed via a series of API methods on the Terra Draw instance itself. All data in the store is represented using GeoJSON. Currently the store only represents data as Features, using the Point, LineString and Polygon geometry types.
By default Terra Draw will use UUID4 to create Feature ids. This is because these ids are broadly unique and, as such, lend themselves well to IDs where would want our features to be uniquely identifiable, potentially across drawing sessions.
It is possible to override this with an approach which aligns with your own requirements. This is done via the `idStrategy`` paramter which is passed at the top level when you create a Terra Draw instance. For example, say we wanted to be able to define our own integer incrementing id strategy, we could do that like so:
const draw = new TerraDraw({
adapter,
modes,
idStrategy: {
isValidId: (id) => typeof id === "number" && Number.isInteger(id),
getId: (function () {
let id = 0;
return function () {
return ++id;
};
})() // Returns a function that returns incrementing integer ids
},
});
You can create any strategy you like, although it advisable to start with one and stick with it consistently.
Important
You can only have one unique id per feature in each Terra Draw instance. Trying to create or add a feature with an id that already exists will throw an error!
It is also possible to check if a given feature with an id exists if needed by:
draw.hasFeature('f8e5a38d-ecfa-4294-8461-d9cff0e0d7f8')
The data contained with the store is exposed via the getSnapshot
method, which returns an array of all given Feature geometries in the Store:
// Get an Array of all Features in the Store
const features = draw.getSnapshot();
We can then filter through this array to get the features we're looking for, as an example only returning TerraDrawPolygonMode
features:
features.filter((feature) => feature.properties.mode === 'polygon')
Features are added to the Store when interacting with the Map using a number of available drawing Modes (such as TerraDrawRectangleMode
or TerraDrawPolygonMode
).
Tip
The getSnapshot
method returns a deep copy of the Store, so you can safely mutate the returned array without affecting the Store.
Features can also be added to the Store programmatically using the addFeatures
method:
// Add a Point to the Store
const result = draw.addFeatures([
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [-1.825859, 51.178867],
},
properties: {
mode: "point",
},
},
]);
console.log(result)
// Will log:
// [{
// id: 'aa5c8826-cbf1-4489-9b31-987c871866af',
// valid: true,
// }]
addFeatures
returns an array of objects providing information about the added features such as the id and the feature was valid. Only valid features are added to the store. This allows developers to handle failure cases how they see fit:
// Create a list of features that did not pass validation
const invalidFeatures = result.filter(({ valid }) => !valid);
// Do something with the information
invalidFeatures.forEach(({ reason }) => {
// ValidationReasonFeatureHasHoles is exposed in the Terra Draw library under ValidationReasons
if (reason === ValidationReasonFeatureHasHoles) {
// sentAnalytics is a fake function that illustrates reacting to a invalid feature
sentAnalytics({
type: 'error',
info: 'Feature that is being added to Terra Draw has holes when it should not'
})
}
});
If you want to get an ID for a feature and create that outside Terra Draw to do something with it later, you can use the getFeatureId
method on the Terra Draw instance, like so:
const id = draw.getFeatureId()
// Add a Point to the Store
draw.addFeatures([
{
id,
type: "Feature",
geometry: {
type: "Point",
coordinates: [-1.825859, 51.178867],
},
properties: {
mode: "point",
},
},
]);
Here setting the mode property will add the feature to that specific mode, so for example in the above instance we added a point to the TerraDrawPointMode
of this instance of Terra Draw.
Important
In the above example we do not pass in an id property, as addFeatures will generate one for us using the idStrategy
provided or the built in one (which defaults to UUID4). You can provide your own id but it must be valid by the idStrategy
you provide. For more info on ids, see the Ids section above.
You can add the mode
property to the Feature dynamically before adding it to Terra Draw:
// Iterate over the points
points.forEach((point) => {
// Set the `mode` property to "point"
point.properties.mode = "point";
});
draw.addFeatures(points);
Note
We have to provide the mode property to the passed feature because otherwise Terra Draw does not know which mode you intended the feature to be added to. Assume a user has two different types of point modes in their application - where would the point be added to? Although it is slightly more verbose, this approach ensures that the features end up in the correct modes they were intended for by the user.
We can also get data at a specific event location, using the getFeaturesAtLngLat
and getFeaturesAtPointerEvent
methods. Find out more about these in the events section of the guides.
To remove Features from the Store use the removeFeatures
or clear
methods:
// Remove Feature from the Store by ID
draw.removeFeatures(["id-1", "id-2", "id-3"]);
// Remove all Features from the Store
draw.clear();
See the TerraDraw
API Docs for more information.
Terra Draw is agnostic to how you want to presist the data created with it. You can store data in a remote database, in IndexedDB, localStorage, or any other storage mechanism you so wish. As a simple example of storing the data in localStorage
, you could take a snapshot and restore it at a later date like so:
const features = draw.getSnapshot()
// We don't want any mid points or selection points so we filter them out
const filteredFeatures = features.filter((f) => !f.properties.midPoint && !f.properties.selectionPoint)
// localStorage can only store strings, so we strinify the features first
localStorage.setItem('terra-draw-data', JSON.stringify(filteredFeatures));
// Later on, perhaps after the user has refreshed.
const retrievedFeatures = localStorage.getItem('terra-draw-data');
if (retrievedFeatures) {
draw.addFeatures(JSON.parse(retrievedFeatures))
}
Guides