Interactive shape editing on top of the Mapbox Maps SDK for React Native
- Example
- Features
- Installation
- Usage
- API Documentation
- Utility scripts
- Known issues and future work
- Contributing
- License
The example app demonstrates almost all of the features of the library, in particular geometry drawing, and creating custom forms for editing metadata associated with geometry.
To run the example app, refer to the instructions in CONTRIBUTING.md.
- Create, edit, and delete different types of GeoJSON geometry:
Point
,LineString
, andPolygon
- Select single or multiple shapes for deletion
- Select single shapes and preview, view, or edit their metadata. Metadata is stored in GeoJSON
"properties"
. - Create custom forms for editing metadata
- Customize the content that appears in metadata previews
- Undo and redo shape editing and deletion changes
- Import GeoJSON into the library for display or editing
- Export GeoJSON from the library for use in the rest of your application
During geometry import operations (see src/util/geometry/io.tsx
):
- Multi-geometry GeoJSON types will be split up.
For example,
MultiLineString
features will be divided intoLineString
features. - Holes will be removed from polygons.
Presently this library is not published to a package repository. You can install it using a Git URL, along with its mandatory peer dependencies, as follows:
yarn add git+https://github.com/newfarms/react-native-mapbox-geometry-editor#development @react-native-mapbox-gl/maps react-native-get-random-values
If you wish to use the default editing controls user interface (<GeometryEditorUI/>
) from this library, then you must also install the peer dependency react-native-vector-icons
.
Run yarn add react-native-vector-icons
, and then follow the additional instructions listed here.
Note that there is no need to set up FontAwesome 5 support with react-native-vector-icons
.
The unofficial Mapbox Maps SDK for React Native, @react-native-mapbox-gl/maps
is a peer dependency.
To use it, you must have a Mapbox API access token, unless you plan to use a different map provider.
See below for more remarks concerning the Mapbox dependency.
The library provides builds to suit different transpilation mechanisms:
// commonjs module system
import { GeometryEditorUI } from 'react-native-mapbox-geometry-editor/lib/commonjs';
// ES6 module system
// Useful for tree-shaking
import { GeometryEditorUI } from 'react-native-mapbox-geometry-editor/lib/module';
// TypeScript
import { GeometryEditorUI } from 'react-native-mapbox-geometry-editor/lib/typescript';
For more information about the build targets, see https://github.com/callstack/react-native-builder-bob#targets
import { GeometryEditorUI } from 'react-native-mapbox-geometry-editor';
The plain import will import the source code of the library, meaning that your code will need to be transpiled under Babel or TypeScript configurations that are compatible with those used to develop the library.
Presently, the example app (example/src/App.tsx
) showcases almost all of the features of the library, and is the best, and most concise, source of documentation.
The library also has Typedoc documentation.
You can run the example by following the instructions in CONTRIBUTING.md.
The library uses the GeoJSON Feature format to communicate geometry with client applications.
GeoJSON Feature objects can be associated with arbitrary metadata, stored under a "properties"
key.
The <GeometryEditorUI/>
graphical interface for the library accepts schema descriptions.
The library uses schema descriptions build forms so that the user can edit geometry metadata in a type-safe manner.
The schema descriptions are generated by the functions passed in the metadataSchemaGeneratorMap
prop of <GeometryEditorUI/>
.
Note that schema descriptions are requested immediately before an interface is opened for viewing or editing metadata, so different schema descriptions can be returned at different times, and can be customized for different GeoJSON Feature objects.
If a schema description returned by a function is not null
, it must be an object of the form parsed by @demvsystems/yup-ast
, subject to restrictions described below.
The schema description must contain a top-level object:
[
['yup.object'],
['yup.required'],
[
'yup.shape',
{
// FIELDS
fieldKey: [...] // FIELD SCHEMA
},
],
]
The FIELDS
portion contains the actual fields of the GeoJSON metadata.
The datatype of a field must be one of the following:
fieldKey: [['yup.mixed'], ['yup.oneOf', ["ARRAY", "OF", "STRINGS" ]],]
: A string-typed enumeration, where the array of possible values must have at least one elementfieldKey: [['yup.string']]
: A stringfieldKey: [['yup.number']]
: A numberfieldKey: [['yup.boolean']]
: A boolean
Fields with datatypes not in the above list will not appear in metadata forms, but can still be present in the metadata of objects imported by the library. The library will only display and allow editing of fields of the supported types, and will leave other fields hidden and untouched. The library will also ignore all properties of metadata objects that are not described in the schema.
The schema description can (and should) contain human-readable text to assist the user:
- Fields can be given human-readable names using the
'yup.label'
attribute. For example,fieldKey: [['yup.boolean'], ['yup.label', 'Field name'],]
gives the field a name of'Field name'
. If a label is not provided, the field name will default to the field's key. - Constraints on fields beyond basic datatype constraints can be associated with custom error messages.
For example, if a field's schema is
fieldKey: [['yup.string'], ['yup.required', 'This field is required'],]
, then if the user does not give the field a value, they will be shown the error message'This field is required'
.
Refer to the example app for an example of a complex metadata schema description.
The library provides the validateMetadata()
utility function for validating the syntax of schema descriptions.
validateMetadata()
can also test whether a given JavaScript object conforms to the input schema description.
Metadata schema descriptions can be given meta information at both the object and field level:
[
['yup.object'],
['yup.required'],
['yup.meta', {...}], // Object-level meta information
[
'yup.shape',
{
fieldKey: [..., ['yup.meta', {...}], ...] // Field schema with field-level meta information
},
],
]
Meta information controls the circumstances under which metadata objects and metadata object fields can be viewed and edited, for instance.
Object-level meta information is described by the MetadataAttributes
interface in src/type/metadata.ts
, whereas field-level meta information is described by the FieldAttributes
interface in src/type/metadata.ts
.
Please read the code documentation comments of these interfaces for descriptions of the available options.
Default values for meta information are provided by the metadataAttributesImpl
and fieldAttributesImpl
validators in src/util/metadata/schema.ts
, so the client application only needs to provide any meta information properties whose values must differ from the defaults.
HTML API documentation for the library can be generated using Typedoc as follows:
- Run
yarn bootstrap
in the root directory of the repository - Run
yarn docs
- Open
docs/src/index.html
in a web browser
fix_winding.js
: A script that corrects the winding order of GeoJSON polygons in GeoJSON data. This script is helpful when preparing data to be imported into the geometry editing library.
The library and its example app need to be updated to the latest versions of their dependencies, in particular:
The Mapbox Maps SDK now supports multiple different map library implementations, and the documentation of this library should be updated to describe how to take advantage of the different map libraries. Presently, this library's example uses MapLibre on Android, and a version of Mapbox prior to v10 on iOS. Other map libraries should work, but need to be tested. On both platforms, the example app is configured to use Mapbox's services as its map provider, but the library could be used with other map servers supported by the Mapbox Maps SDK for React Native.
The <GeometryEditorUI/>
component renders a graphical user interface for editing geometry on top of a map.
(It also renders the map.)
The user interface is not customizable aside from some coarse style settings.
The library also exposes a <GeometryEditor/>
component that renders a map, but no geometry editing controls.
As a result, this component can only be used to display geometry.
In the future, either <GeometryEditorUI/>
should accept more user interface customization options, or <GeometryEditor/>
should expose a full programmatic interface for editing geometry.
Otherwise, it will continue to be difficult to adapt geometry editing controls to the look and feel of the surrounding app.
In the future, this library should be published to a package repository, such as NPM, for easier use with package managers.
- In some client applications, while running in development mode, the library will emit the following warning:
"[mobx] Derivation observer_StoreProvider is created/updated without reading any observable value"
Refer to the comments insrc/state/StoreProvider.tsx
for details. - There are inconsistent performance issues with React Native Paper-based dialogs. Presently these issues seem to be observed only on iOS, and only with dialogs that need to manage some local state. A possibly related issue may be callstack/react-native-paper#2157
- Enumeration and boolean-typed geometry metadata fields are always given values during metadata creation or editing operations, even if the client application marks the fields as
'yup.optional'
(optional) fields, or marks the fields as non-creatable or non-editable. This is not necessarily a problem, since these fields will therefore always be given valid values. The library is still able to handle enumeration and boolean-typed fields that have missing or invalid values, however, such as when rendering metadata created outside the library.
- Geometry rendering on an Android emulator may exhibit visual problems such as rendering points in grey instead of in their desired colours. Zooming in and out on the map may make colours randomly appear and disappear.
- Draggable points/vertices will usually render underneath all other geometry (https://github.com/react-native-mapbox-gl/maps/issues/806).
- To drag an editable point, it may be necessary to first tap on the point (press and release) before pressing and holding to drag the point.
- Editable points may snap back to their original positions while or after being dragged (https://github.com/react-native-mapbox-gl/maps/issues/1117).
- To draw a new point, it may be necessary to first tap on the map, to switch focus to the map, after having tapped on a geometry object or on a user interface element. In other words, two taps on the map may be required to draw a new point.
See the contributing guide to learn how to run the example app, and how to contribute to the repository.
MIT