Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Port Meta Tests and Functions #12

Closed
20 tasks done
baparham opened this issue Dec 12, 2020 · 26 comments
Closed
20 tasks done

Port Meta Tests and Functions #12

baparham opened this issue Dec 12, 2020 · 26 comments
Assignees

Comments

@baparham baparham self-assigned this Dec 12, 2020
@lukas-h
Copy link
Member

lukas-h commented Nov 2, 2021

Hi @baparham, any updates?

@baparham
Copy link
Member Author

baparham commented Nov 8, 2021

No 😳 it's been a rather busy year, but I can spend some time on this, I've been wanting to get back into flutter/dart development lately so your ping is timely. I'll block out some time this week to devote here.

@lukas-h
Copy link
Member

lukas-h commented Nov 8, 2021

Nice! I'm also slowly starting to work on other parts of the library again, e.g. null-safety support which is more than overdue

@tobrun
Copy link
Collaborator

tobrun commented Nov 20, 2021

The most useful meta function to now work on will be coordEach. It's heavily used by other turf functions. Happy to work on that one but might tip my toes in the water with an easy meta function to get started :)

edit: have implementation ready for propEach but depends on merging #13 first

@lukas-h
Copy link
Member

lukas-h commented Nov 21, 2021

When implementing the functions, please let's keep in mind that my GeoJSON implementation follows a very strict class representation of the RFC standard. I would prefer to have stricter types for the functions, e.g.:

- void geomEach(dynamic geoJSON, GeomEachCallback callback) {
+ void geomEach(GeoJSONObject geoJSON, GeomEachCallback callback) {

also, see my comments here: #24 and here: #14 (comment)


here you can see all the classes and their inheritance in a tree diagram:
GeoJSON Polymorphism (1)

@baparham
Copy link
Member Author

I agree @lukas-h that these should be typed stricter. Currently the GeoJSONObject doesn't include features, so perhaps that's the first step, is to bring that abstract class up to snuff?

I can try and implement this in a new PR, I'll paste that in here if I get something that works

@baparham
Copy link
Member Author

It seems like I can use some runtime typechecking, and casting with as but I think that means that most of the geomEach function will get rewritten since dart can't handle the way turf.js used the isFeature[Collection] booleans to handle runtime type checking.

e.g. this works:

  bool isFeatureCollection = instanceof(geoJSON, FeatureCollection);
  bool isFeature = instanceof(geoJSON, Feature);
  int stop = 1;
  if (isFeatureCollection) {
    stop = (geoJSON as FeatureCollection).features.length;
  }

but then further down we have things like this that do not work:

  for (var i = 0; i < stop; i++) {
    geometryMaybeCollection = (isFeatureCollection
        ? geoJSON.features[i].geometry
        : (isFeature ? geoJSON.geometry : geoJSON));
    featureProperties = (isFeatureCollection
        ? geoJSON.features[i].properties
        : (isFeature ? geoJSON.properties : {}));
    featureBBox = (isFeatureCollection
        ? geoJSON.features[i].bbox
        : (isFeature ? geoJSON.bbox : null));
    featureId = (isFeatureCollection
        ? geoJSON.features[i].id
        : (isFeature ? geoJSON.id : null));
    isGeometryCollection = (geometryMaybeCollection != null)
        ? geometryMaybeCollection.type == GeoJSONObjectTypes.geometryCollection
        : false;
...

since features is only available on the subclass FeatureCollection and not on the abstract GeoJSONObject class :-(

I think I'll just go ahead and refactor this code to meet our intended purpose, be more readable, yet diverge from simple 1:1 implementation mapping from turf.js

@lukas-h
Copy link
Member

lukas-h commented Nov 22, 2021

@baparham That's a little bit more difficult than that..

Not all subtypes of GeoJSONObject should have the same attributes following the RFC standard.
For example Point, Polygon, etc. have coordinates, GeometryCollection has geometries, Feature has geometry, FeatureCollection has features.

Maybe we need to work more with type casting as or checking is for subclasses of GeoJSONObject..

@baparham
Copy link
Member Author

I think it works to simply trust the type field and then cast as needed using the as operator. I tried to make a simple proof of concept, but my gh credentials locally are blocking me from pushing 🙄 so give me a couple more minutes, haha

@baparham
Copy link
Member Author

baparham commented Nov 22, 2021

what about this method? #29

It's soooo ugly and needs to be refactored to use more focused helper functions I think, but to test the concept, I think it works.

@baparham
Copy link
Member Author

baparham commented Nov 22, 2021

here you can see all the classes and their inheritance in a tree diagram: GeoJSON Polymorphism (1)

Regarding naming, does GeometryObject and Geometry work as replacements for Geometry and GeometryType (respectively) perhaps to try and more closely parallel the GeoJSONObject?

[edit:] I mean, I am not really a fan of redundant naming conventions like *Object and *Type but we have to differentiate these higher order types somehow

@lukas-h
Copy link
Member

lukas-h commented Nov 22, 2021

I see why the naming can be a little bit confusing. We should definitely think of a naming convention.

  • I think GeoJSONObject can stay as it is.
  • Geometry to GeometryObject seems okay!
  • Changing GeometryType to just Geometry is confusing, I think.
  • We should find a new name for CoordinateType as well.

Background: the RFC standard defines different groups of GeoJSON object types:

  • GeoJSON Objects, currently covered by GeoJSONObject, basically all usable GeoJSON types: [Point, Multipoint, Polygon, MultiPolygon, LineString, MultiLineString, GeometryCollection, Feature, FeatureCollection]
  • Geometry Objects, currently covered by Geometry, a subset of the types that actually include some kind of geometry(/-ies): [Point, Multipoint, Polygon, MultiPolygon, LineString, MultiLineString, GeometryCollection]
  • Additionally I created another abstract type for all classes that have a coordinates attributes, which I called GeometryType (which extends Geometry): [Point, Multipoint, Polygon, MultiPolygon, LineString, MultiLineString]

@tobrun
Copy link
Collaborator

tobrun commented Dec 13, 2021

Finally getting back to my original attempt of implementing coordEach to unblock the other APIs. I have it most of it ported from JS implementation but I'm noticing a difference in setup between Dart vs JS setup of geomEach. Am I correct to assume, we rather want the _ShortCircuit approach as with geomEach? and I should drop the JS specific optimizations as commented in This logic may look a little weird?

@lukas-h
Copy link
Member

lukas-h commented Dec 14, 2021

@tobrun Maybe let's first implement it like the geomEach.
But later on we could test the performance of different implementation in a benchmark suite, as proposed in #32

@baparham
Copy link
Member Author

I'll get started on flattenEach for now

@lukas-h
Copy link
Member

lukas-h commented Dec 15, 2021

I have a proposal:

To have all the meta functions like as independent / standalone functions, seems to me very "non-OOP", not "Dart-like", but instead like a more common style for JavaScript.

My idea is, to glue the meta functions (at for least those, that actually make sense) to the GeoJSON classes directly.

For the user, the API could look like this:

var featColl = FeatureCollection.fromJson(json);

featColl.geomEach(
// ...
);

I started to implement this for the geomEach function in two different ways:

  1. directly inside each of the classes inheriting from GeoJSONObject in geojson.dart:

    void geomEachImpl1(GeomEachCallback callback);

  2. as extension methods on all of the classes in this file:

    void geomEachImpl2(GeomEachCallback callback) {

You can see the changes in this commit: b02af15

It's not yet debugged, and there could still be some hurdles with the implementation,
but it already illustrates my idea very well.


Please let me know what you think!

@baparham @tobrun

@tobrun
Copy link
Collaborator

tobrun commented Dec 15, 2021

To have all the meta functions like as independent / standalone functions, seems to me very "non-OOP", not "Dart-like", but instead like a more common style for JavaScript.

I like the idea, the only things I could counter to this proposal are:

  • we would be adding many APIs to GeoJSON classes that might make it more complicated for developers to navigate (eg. code completion). GeoJSON, for non-geospatial developers, is already hard to initially grasp conceptually.
  • the implementation wouldn't be a direct match to the GeoJSON spec anymore

I don't think these reasons justify not doing it but I did want to provide that angle as input.

as extension methods on all of the classes in this file

I would vote for an extension approach. In the past I had libraries that were dependent on the java geojson implementation but weren't allowed to pull in turf-java due to binary size constraints. Having them exposed as extension functions will give us the flexibility to pull out geojson into an independent library and have a separate extension function library to provide turf functionality. Are there any downsides to use extension functions?

@baparham
Copy link
Member Author

I agree with @tobrun that we don't want to arbitrarily pollute the namespace with stuff people may not care about, and your extensions approach seems to address this concern directly, that if one wants to use the syntactic sugar, they can import the extensions.

I am not very familiar with extensions as a concept, but I do not immediately see downsides. Perhaps if someone is copying example code that takes advantage of the extensions, they may not do the extra import needed, and I don't know if their IDE would suggest such an import.

@lukas-h
Copy link
Member

lukas-h commented Dec 15, 2021

I totally agree with both of you! Let’s keep the GeoJSON object representation in geojson.dart slim!

But there’s one catch with the extension method approach:
We can’t add method overrides of a function declaration (e.g. void geomEach…);) in GeoJSONObject to each of the extensions. We can only put concrete method implementations into the extensions.
This causes some weird type casting: as pointed out by @baparham here:
b02af15#r61986415

@lukas-h
Copy link
Member

lukas-h commented Dec 15, 2021

Are there any downsides to use extension functions?

  • what I described in my other comment
  • Possibly this:

Perhaps if someone is copying example code that takes advantage of the extensions, they may not do the extra import needed, and I don't know if their IDE would suggest such an import.

@baparham
Copy link
Member Author

Is there a way to use the actual implementation via the extension method?

import 'package:turf/src/meta.dart';
import 'package:turf/helpers.dart';

extension GeomEachGeoJSONObject on GeoJSONObject {
  void geomEachImpl2(GeomEachCallback callback) {
    geomEach(this, callback);
  }
}

@baparham
Copy link
Member Author

I don't like that having the implementation in two places

@lukas-h
Copy link
Member

lukas-h commented Dec 16, 2021

I don't like that having the implementation in two places

Totally agree. Tried to incorporate that in this commit: 3243325

Is there a way to use the actual implementation via the extension method?

import 'package:turf/src/meta.dart';
import 'package:turf/helpers.dart';

extension GeomEachGeoJSONObject on GeoJSONObject {
  void geomEachImpl2(GeomEachCallback callback) {
    geomEach(this, callback);
  }
}

I'm not sure, if your (@baparham) idea here using the geomEach top level function or my if implementation in 3243325 is better.

  • my implementation skips the type checking in the top level function geomEach
  • your implementation is much cleaner and does not expose all of geomEach's implementation in meta.

@lukas-h
Copy link
Member

lukas-h commented Dec 19, 2021

  • getCoord
  • getCoords
  • getGeom
  • getType

what about the other functions inside turf invariant? I guess @baparham you took the tasks from the README.
I think, we should update the README, because since then, new modules have been created

In my opinion, most of the functions in turf invariant are not even needed in Dart, because of the better type system.
For example getGeom, which just takes a Feature as a param and returns its geometry.

@baparham
Copy link
Member Author

baparham commented Dec 19, 2021 via email

@lukas-h
Copy link
Member

lukas-h commented Apr 1, 2022

Will close this, because we are done with this milestone 🎉

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants