-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature data structures on geobase #138
Labels
Comments
navispatial
added
enhancement
New feature or request
🌐 geobase
Related to the code package "geobase"
labels
Aug 4, 2022
This was referenced Aug 4, 2022
navispatial
added a commit
that referenced
this issue
Aug 6, 2022
navispatial
added a commit
that referenced
this issue
Aug 6, 2022
Implemented in geobase 0.3.0-dev.2.
/// A feature is a geospatial entity with [id], [properties] and [geometry].
///
/// Some implementations may contain also [custom] data or "foreign members"
/// containing property and geometry objects.
///
/// Feature objects have an optional primary [geometry] of [T].
///
/// Supports representing data from GeoJSON (https://geojson.org/) features.
class Feature<T extends Geometry> extends Bounded {
final Object? _id;
final T? _geometry;
final Map<String, Object?> _properties;
/// A feature of optional [id], [geometry] and [properties].
///
/// An optional [id], when given, should be either a string or an integer
/// number.
///
/// An optional [geometry] of [T], when given, is the primary geometry of the
/// feature.
///
/// An optional [properties] defines feature properties as a map with data
/// similar to a JSON Object.
const Feature({
Object? id,
T? geometry,
Map<String, Object?>? properties,
}) : _id = id,
_properties = properties ?? const {},
_geometry = geometry;
/// A feature from optional [id], [geometry], [properties] and [custom] data.
///
/// An optional [id], when given, should be either a string or an integer
/// number.
///
/// An optional [geometry] is a callback function providing the content of
/// geometry objects. The geometry of [T] named "geometry" or the first
/// geometry of [T] without name is stored as the primary geometry. Any other
/// geometries are stored as custom or "foreign members".
///
/// An optional [properties] defines feature properties as a map with data
/// similar to a JSON Object.
///
/// Use an optional [custom] parameter to set any custom or "foreign member"
/// properties.
///
/// An example to create a feature containing a point geometry, the returned
/// type is `Feature<Point>`:
///
/// ```dart
/// Feature<Point>.build(
/// id: '1',
/// geometry: (geom) => geom.point([10.123, 20.25]),
/// properties: {
/// 'foo': 100,
/// 'bar': 'this is property value',
/// 'baz': true,
/// },
/// );
/// ```
factory Feature.build({
Object? id,
WriteGeometries? geometry,
Map<String, Object?>? properties,
WriteProperties? custom,
}) {
// optional data to be built as necessary
T? primaryGeometry;
Map<String, Object?>? builtCustom;
// use geometry builder to build any geometry (primary + foreign) objects
if (geometry != null) {
var index = 0;
GeometryBuilder.build(
geometry,
to: (Geometry geometry, {String? name}) {
if (name == 'geometry' && geometry is T) {
// there was already one geometry as "primary", move it to custom
if (primaryGeometry != null) {
(builtCustom ??= {})['#geometry$index'] = primaryGeometry;
index++;
}
// use the geometry of T named 'geometry' as the primary geometry
primaryGeometry = geometry;
} else if (name == null && primaryGeometry == null && geometry is T) {
// OR the first geometry of T without name as the primary geometry
primaryGeometry = geometry;
} else {
// a geometry with name, add to the custom map
(builtCustom ??= {})[name ?? '#geometry$index'] = geometry;
index++;
}
},
);
}
// use property builder to build any foreign property objects
if (custom != null) {
builtCustom ??= {};
PropertyBuilder.buildTo(custom, to: builtCustom!);
}
// create a custom feature with "foreign members" OR a standard feature
return builtCustom != null
? _CustomFeature(
id: id,
geometry: primaryGeometry,
properties: properties,
custom: builtCustom!,
)
: Feature(
id: id,
geometry: primaryGeometry,
properties: properties,
);
}
/// An optional identifier (a string or number) for this feature.
Object? get id => _id;
/// An optional primary geometry of [T] for this feature.
T? get geometry => _geometry;
/// Required properties for this feature (allowed to be empty).
Map<String, Object?> get properties => _properties;
/// Optional custom or "foreign member" properties and geometries as a map.
///
/// The primary geometry and properties data (like `geometry` and `properties`
/// objects in GeoJSON features) are accessed via [geometry] and [properties].
/// However any custom property and geometry data outside those members is
/// stored in this member.
Map<String, Object?>? get custom => null;
// todo: ==, hashCode, toString
}
class _CustomFeature<T extends Geometry> extends Feature<T> {
final Map<String, Object?> _custom;
/// A feature of optional [id], [geometry], [properties] and [custom] data.
///
/// An optional [id], when given, should be either a string or an integer
/// number.
///
/// An optional [geometry] of [T], when given, is the primary geometry of the
/// feature.
///
/// An optional [properties] defines feature properties as a map with data
/// similar to a JSON Object.
///
/// Use an optional [custom] parameter to set any "foreign member" properties
/// and geometries as a map.
const _CustomFeature({
super.id,
super.geometry,
super.properties,
required Map<String, Object?> custom,
}) : _custom = custom;
@override
Map<String, Object?> get custom => _custom;
}
/// A feature collection with a series of features.
///
/// Some implementations may contain also [custom] data or "foreign members"
/// containing property objects.
///
/// Supports representing data from GeoJSON (https://geojson.org/) features.
class FeatureCollection<E extends Feature> extends Bounded {
final List<E> _features;
final Map<String, Object?>? _custom;
/// A feature collection with a series of [features].
const FeatureCollection(List<E> features)
: _features = features,
_custom = null;
const FeatureCollection._(this._features, this._custom);
/// A feature collection from the content provided by [features].
///
/// Feature objects on a collection have an optional primary geometry of [T].
///
/// Only [Feature] objects of `Feature<T>` provided by [features] are built,
/// any other objects are ignored.
///
/// An optional expected [count], when given, specifies the number of feature
/// objects in a content stream. Note that when given the count MUST be exact.
///
/// Use an optional [custom] parameter to set any custom or "foreign member"
/// properties.
///
/// An example to create a feature collection with feature containing point
/// geometries, the returned type is `FeatureCollection<Feature<Point>>`:
///
/// ```dart
/// FeatureCollection.build<Point>(
/// count: 2,
/// (features) => features
/// ..feature(
/// id: '1',
/// geometry: (geom) => geom.point([10.123, 20.25]),
/// properties: {
/// 'foo': 100,
/// 'bar': 'this is property value',
/// 'baz': true,
/// },
/// )
/// ..feature(
/// id: '2',
/// // ...
/// ),
/// );
/// ```
static FeatureCollection<Feature<T>> build<T extends Geometry>(
WriteFeatures features, {
int? count,
WriteProperties? custom,
}) {
// todo: use optional count to create a list in right size at build start
// build any feature objects on a list
final builder = _FeatureBuilder<T>();
features.call(builder);
// build any custom properties on a map
final builtCustom =
custom != null ? PropertyBuilder.buildMap(custom) : null;
// create a feature collection with feature list and optional custom props
return FeatureCollection<Feature<T>>._(builder.list, builtCustom);
}
/// All feature items in this feature collection.
List<E> get features => _features;
/// Optional custom or "foreign member" properties as a map.
///
/// The primary feature items are accessed via [features]. However any custom
/// property data outside it is stored in this member.
Map<String, Object?>? get custom => _custom;
// todo: ==, hashCode, toString
}
class _FeatureBuilder<T extends Geometry> implements FeatureContent {
final List<Feature<T>> list;
_FeatureBuilder() : list = [];
@override
void feature({
Object? id,
WriteGeometries? geometry,
Map<String, Object?>? properties,
Box? bbox,
WriteProperties? custom,
}) {
list.add(
Feature<T>.build(
id: id,
geometry: geometry,
properties: properties,
custom: custom,
),
);
}
@override
void featureCollection(
WriteFeatures features, {
int? count,
Box? bbox,
WriteProperties? custom,
}) {
// nop (feature collection are not features)
}
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
Related to similar issue #133 on geometries.
Implement data structures for
Feature
andFeatureCollection
that are in sync with content interfaces,FeatureContent
andGeometryContent
.The text was updated successfully, but these errors were encountered: