This release introduces the **streamlined API** we've announced in 0.1.10.
Most importantly, we've simplified the type system. OpenStreetMap features are represented as *nodes*, *ways* and *relations*, and GeoDesk has corresponding `Node`, `Way` and `Relation` interfaces, which derive from the `Feature` supertype. In 0.2, these subtypes still exist as marker interfaces, but their specialized methods have moved to the supertype. This means that all features can simply be treated as a `Feature`, which removes the need for casting and awkward generics. When working with feature sets, you'll no longer need to declare `Features<?>` or `Features<Node>` -- you'll simply use `Features`.
This change aligns more closely with how users think about OSM data. Most points-of-interest can be modeled as nodes, ways or relations -- for example, a museum could be marked as a single point, a simple polygon (as a closed way) or a building with a courtyard (requiring a multipolygon relation). In those cases where the OSM type matters, use `.isNode()`, `.isWay()` or `.isRelation()` (or `type()`).
We've prepared a [Migration Guide](https://github.com/clarisma/geodesk/blob/main/doc/migrating.md) to ensure your upgrade goes smoothly.
Breaking changes
- The `Features` interface is no longer generic. Iterating over `Features` always
returns `Feature` objects.
- The typed methods (`memberNodes()`, `parentRelations()`, etc.) have been
removed. To constrain a type, use `members().nodes()`, `parents().relations()`,
or include the feature type in a query string (`members("n")`, `parents("r")`).
- All classes from `com.geodesk.core` (such as `Box` and `Heading`) have moved to `com.geodesk.geom`
Enhancements
- Applying spatial filters is now much simpler. Instead of using the `Filters` factory class, filters can now be invoked directly on the `Features` object.
So instead of
`biotopes.select(Filters.coveredBy(area))`
simply use
`biotopes.coveredBy(area)`
Note that several spatial filter methods have changed names:
`contains()` -> `containing()`
`crosses()` -> `crossing()`
`intersects()` -> `intersecting()`
`overlaps()` -> `overlapping()`
`touches()` -> `touching()`
These changes make your code read more naturally:
`bridges.crossing(river)`
`adminAreas.touching(county)`
This change also resolves the ambiguity concerning `contains()`:
- `Features.contains(Object)` checks if the given feature is part of this feature set.
- `Features.containing(Feature)` returns the features whose geometry contains the given feature.
Deprecations
- The `Filters` factory class has been deprecated and may be removed in future releases.
The preferred way to use filters is to call the filter methods on `Features`, as discussed above.