Skip to content

Possibility for local tangent plane support? #269

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

Open
CallumHoughton opened this issue Mar 3, 2025 · 2 comments
Open

Possibility for local tangent plane support? #269

CallumHoughton opened this issue Mar 3, 2025 · 2 comments
Labels
enhancement New feature or request 🌐 geobase Related to the code package "geobase"

Comments

@CallumHoughton
Copy link

Hello,

First off - thank you for this library! It's helped immensly in a GIS data collection app and has cut down on some of the GIS related code I needed to port over when I migrated to Flutter at my workplace.

The recent addition of ECEF coordinates has been great too. An extension I had to make in my local code is to convert ECEF coordinates into a local tangent plane (usually called east north up in GIS things): https://en.wikipedia.org/wiki/Local_tangent_plane_coordinates#:~:text=It%20consists%20of%20three%20coordinates,%2C%20down%20(NED)%20coordinates.

This is because given a list of polygon latitude longitudes coordinates (a polygon in this case is just a simple one with one set of exterior coordinates) I want to calculate the 2D area of this polygon. You can't just use the shoelace formula on ECEF cartesian coordinates (I'm still trying to fully understand why but my understanding is that ECEF coordinates are not planar and will warp). So you usually convert the ECEF coordinates into a local tangent plane space (where 0,0 of the plane is first coordinate of the polygon) and then the usual area calculations work. This will only work well for lengths of like less than 250km (any area of the earth you can approximate accurately to a plane) - for larger ones you get into the realm of using UTM coordinate systems.

I thought I'd pass this over anyways as it may be useful and might be something you want to add to the library :)

  static double areaTangentPlane(gb.Geometry geometry) {
    final enuPositions = <gb.Position>[];

    final positions = getGeometryPositions(geometry);
    if (positions.isEmpty || positions.length < 3) {
      return 0;
    }

    final referenceGeo = gb.Geographic(lon: positions.first.x, lat: positions.first.y, elev: positions.first.z);
    final referenceECEF = referenceGeo.toGeocentricCartesian();

    for (var pos in positions) {
      final geo = gb.Geographic(lon: pos.x, lat: pos.y, elev: pos.z);
      final ecef = geo.toGeocentricCartesian();
      final enu = ENU.fromECEF(ecef, referenceECEF, LatLng(referenceGeo.lat, referenceGeo.lon));

      enuPositions.add(gb.Position.create(x: enu.east, y: enu.north));
    }

    final area = gb.PositionSeries.from(enuPositions, type: gb.Coords.xyz).signedArea2D().abs();
    return area;
  }


class ENU {
  final double east;
  final double north;
  final double up;

  ENU(this.east, this.north, this.up);

  factory ENU.fromECEF(gb.Position ecef, gb.Position reference, LatLng referenceLatLng) {
    double dx = ecef.x - reference.x;
    double dy = ecef.y - reference.y;
    double dz = ecef.z - reference.z;

    double lat0 = referenceLatLng.latitude * pi / 180.0; // Convert to radians
    double lon0 = referenceLatLng.longitude * pi / 180.0;

    double sinLat = sin(lat0), cosLat = cos(lat0);
    double sinLon = sin(lon0), cosLon = cos(lon0);

    // Convert to local ENU coordinates
    double east = -sinLon * dx + cosLon * dy;
    double north = -sinLat * cosLon * dx - sinLat * sinLon * dy + cosLat * dz;
    double up = cosLat * cosLon * dx + cosLat * sinLon * dy + sinLat * dz;

    return ENU(east, north, up);
  }
}
@navispatial
Copy link
Member

Thanks! This sounds interesting, might fit to the geodesy sub package of the geobase package.

The goal is to finalize 1.5.0 version (that should be otherwise quite ready but lacking some doc updates) by end of March.

I look this again next week...

@CallumHoughton
Copy link
Author

Sweet! I've also implemented a similar principle but using a Latitude Longitude -> UTM conversion which is more accurate for larger areas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request 🌐 geobase Related to the code package "geobase"
Projects
None yet
Development

No branches or pull requests

2 participants