Skip to content

latlong2 expectations #2206

Open
Open

Description

@ThexXTURBOXx

What do you want implemented?

Sorry for hijacking this issue thread like this, but it's probably best to ask about this publicly.
I have taken over maintenance of the latlong2 package today and would like your opinion on whether you need something changed or whether something does not work correctly and needs fixing in that package.
flutter_map is by far the most popular package depending on it and, thus, it made sense to me to somehow involve you in the process of finalising a stable 1.0.0 release.
jifalops has published version 0.10.0 yesterday (the first update since 2 years) and I pushed a "hotfix" 0.10.1 just a few minutes ago.

TL;DR: Please let me know if you have any wishes/feature requests for latlong2 :)

What other alternatives are available?

No response

Can you provide any other information?

No response

Activity

  1. monsieurtanuki commented on Apr 28, 2026

    @monsieurtanuki
    Contributor

    Hi @ThexXTURBOXx!

    While we're talking about latlong2 and version 0.10.1, I've just realized (flutter/flutter#185676 (comment)) that below 1.0.0, "minor" changes are considered "major", and therefore "breaking". For instance, 0.10.1 doesn't match ^0.9.1.

    If I had to answer your initial question quickly, I'd say that we have troubles with "latlong segments" and their direction.
    If we go from -179 to 179, does it mean 2 degrees west or 358 degrees east? Should it be latlong2's problem?

    I'll have another look at the code and I'll get back to you.

  2. ThexXTURBOXx commented on Apr 28, 2026

    @ThexXTURBOXx
    ContributorAuthor

    While we're talking about latlong2 and version 0.10.1, I've just realized (flutter/flutter#185676 (comment)) that below 1.0.0, "minor" changes are considered "major", and therefore "breaking". For instance, 0.10.1 doesn't match ^0.9.1.

    I thought that this was common knowledge! But yes, indeed, I personally would have labelled version 0.10.0 rather 0.9.2 as it did not really break anything. Well, the new hashCode could theoretically break stuff, but I doubt it, personally.
    "The old maintainer" published version 0.10.0 quickly before they transferred the repo over to my profile.
    So - sorry for the troubles here. This will be a lot better when 1.0.0 would eventually be published.

    If I had to answer your initial question quickly, I'd say that we have troubles with "latlong segments" and their direction.
    If we go from -179 to 179, does it mean 2 degrees west or 358 degrees east? Should it be latlong2's problem?

    Thanks for the comment! I see... I will think about whether there is something I could change in latlong2 for this or whether it is only worth some documentation.

    I'll have another look at the code and I'll get back to you.

    No worries and take your time, please!

    Besides: 1.0.0-dev.0 is already published over at pub.dev. It contains a major revamp of the entire codebase with quite a lot of (most likely) breaking changes.
    All of them should be fixable through renames, though.
    This is just to warn you beforehand already!

  3. monsieurtanuki commented on Apr 29, 2026

    @monsieurtanuki
    Contributor

    Thanks for the comment! I see... I will think about whether there is something I could change in latlong2 for this or whether it is only worth some documentation.

    It's not a matter of documentation - unless you just mean to say "btw we only support trips from Tokyo to Los Angeles via Europe".

    Obviously the problem is not with coordinates, but with list of coordinates.

    One way to solve it would be to add an "int worldNumber" field in LatLng. Would start with 0, then - if relevant - would be set to 1 or -1 for the next coordinate (if beyond 180, respectively -180). But that would take extra space for all LatLng objects. Not good.

    Another way would be to accept values beyond -180, 180: it wouldn't take more space as there wouldn't be an extra field.

    Or create a new LatLngList object, that would help manage that "which world are we talking about?" feature. That wouldn't be a breaking change.

    The goal being something like: "I want to list of LatLng without confusion, e.g. to support round the world yacht races".

  4. JaffaKetchup commented on Apr 29, 2026

    @JaffaKetchup
    Member

    Hey, thanks for opening this, glad to see its still in safe hands!

    I would say that I don't think it's for LatLng to care about what 'world' it's in. LatLng is a mathematical library for representation of coordinates that can also do some geographic maths based on coordinates - I don't know how the distance calculations handle 'worlds', but I'm making the probably inaccurate assumption that they assume the shortest path where no heading is provided, and if a heading is provided to get an offset then it should work as expected.
    The 'world' is a very flutter_map construct and I don't think we should 'force' down our requirements onto external libraries - we already have LatLngBounds for example.

    I could get behind something like LatLngList and a reworked LatLngBounds in the flutter_map core.

  5. added
    general[DEPRECATED] This issue is better suited for Discussions or Discord
    dependencies[CI] Pull requests that update a dependency file
    on Apr 29, 2026
  6. ibrierley commented on Apr 29, 2026

    @ibrierley
    Contributor

    I guess the ability to have a latlng outside the normal bounds may be within the remit of LatLng2 though ? i.e if its -95,-185 or something, which could give a hint, although maybe a bit hacky ? Also I think previously we've thought it would be useful for different crs's (eg pixel flat game world crs, was it CrsSimple ?) to be able to reuse this in flutter_map (could there be a BoundlessLatLng method that skips the min/max checks?) . I'm a bit out of touch if that's still the case though, or if it would break other stuff!

  7. monsieurtanuki commented on Apr 29, 2026

    @monsieurtanuki
    Contributor

    @JaffaKetchup I see things differently, as there are already classes in latlng2, like Path, that definitely would need the concept of world:

    class Path<T extends LatLng> {class Path<T extends LatLng> {
     /// Coordinates managed by this class
     final List<T> _coordinates;
    
     /// For [Distance] calculations
     final Distance _distance = const Distance();

    And distances are computed "in a single world":

      double distance(final LatLng p1, final LatLng p2) {
        var a = equatorRadius,
            b = polarRadius,
            f = flattening; // WGS-84 ellipsoid params
    
        var l = p2.longitudeInRad - p1.longitudeInRad; // <======= here, it's problematic
        var u1 = math.atan((1 - f) * math.tan(p1.latitudeInRad));
        var u2 = math.atan((1 - f) * math.tan(p2.latitudeInRad));

    As a "minor breaking change", I would consider a new method called double distanceWestEast(final LatLng west, final LatLng east), where the longitude difference would be computed correctly. Perhaps deprecating distance too.

    Perhaps also the concept of addWest and addEast for Path.

    To be continued...

    That said, the process could be: let's improve things in flutter_map first, then see how relevant that would be in latlong2 too.

  8. ThexXTURBOXx commented on Apr 29, 2026

    @ThexXTURBOXx
    ContributorAuthor

    The 'world' is a very flutter_map construct and I don't think we should 'force' down our requirements onto external libraries

    Understandable. On the other hand, I also believe that this is something most dependents are somewhat "expecting" from the package.
    This is, however, where this (previous) comment kicks in:

    One way to solve it would be to add an "int worldNumber" field in LatLng. Would start with 0, then - if relevant - would be set to 1 or -1 for the next coordinate (if beyond 180, respectively -180). But that would take extra space for all LatLng objects. Not good.

    Indeed, this was the first thing I was thinking about when thinking through this.
    Of course, there could be a subclass for LatLng, which does have that field. But this is not only sub-optimal, but also somehow weird to think about, I believe...

    I already thought about improving the current distance function before - by adding a third (optional named) parameter that could be an enum like this:

    enum SegmentDirection {
      /// Always picks the shortest angular path. May cross the antimeridian.
      shortestPath,
      /// Always travels eastward (increasing longitude, wrapping at 180).
      eastward,
      /// Always travels westward (decreasing longitude, wrapping at -180).
      westward,
    }

    Does this make sense? Is there something crucial I am missing?
    I should also test how the current approach works with distances that cross either pole and whether something like the enum above would also be needed/useful for those edge cases.

    I guess the ability to have a latlng outside the normal bounds may be within the remit of LatLng2 though ?

    Yes, version 0.10.0 added a getter LatLng#isValid, which does exactly this. Although, it is not quite correct (IMHO) as it happily accepts both latitude -90 and 90 as valid as well as both longitude -180 and 180.
    Hence, there is still some ambiguity for some "evil" inputs.

    could there be a BoundlessLatLng method that skips the min/max checks?

    0.9.1 should have already removed the assertions two years ago - if there is something which still does not work, please let me know and I will fix it in another interim 0.10.2 release.

  9. monsieurtanuki commented on Apr 29, 2026

    @monsieurtanuki
    Contributor

    @ThexXTURBOXx Quickly said: regarding SegmentDirection, you forgot the current behavior, that may be called "lazy" or "greenwich".

  10. monsieurtanuki commented on Apr 30, 2026

    @monsieurtanuki
    Contributor

    @ThexXTURBOXx Regarding distances, make sure that limit cases can be asked and do work, like the size of the equator [(0,-180),(0,180)].
    Would it make sense to also maintain "strictlyEastward" and "strictlyWestward", in case you want to compute the size of equator with [(0,0),(0,0))?
    Instead of numerous "switch", a solution may be to implement just one method in SegmentDirection, called getLongitudeDelta(LatLng latLng1, LatLng latLng2).
    Could even be in an abstract class, implemented by SegmentDirection: we'd have the added value of enum and the flexibility of abstract classes. That's only if we want to give developers more flexibility that they probably need, in case they find a new typical case to implement.

  11. ThexXTURBOXx commented on Apr 30, 2026

    @ThexXTURBOXx
    ContributorAuthor

    Quickly said: regarding SegmentDirection, you forgot the current behavior, that may be called "lazy" or "greenwich".

    True, thank you! Even though, I really like "greenwich", I feel like I should call it "lazy" instead in order to not confuse new users too much...

    Regarding distances, make sure that limit cases can be asked and do work, like the size of the equator [(0,-180),(0,180)].

    Will do - all of such edge cases will go into the test suite.

    Would it make sense to also maintain "strictlyEastward" and "strictlyWestward", in case you want to compute the size of equator with [(0,0),(0,0))?

    Also a good idea. I am guessing this indeed makes sense and there is an issue, which is about something similar: ThexXTURBOXx/dart-latlong#17
    Even though, it is not really about latlong2, it shows nonetheless that something like this would be nice to have.

    Instead of numerous "switch", a solution may be to implement just one method in SegmentDirection, called getLongitudeDelta(LatLng latLng1, LatLng latLng2).
    Could even be in an abstract class, implemented by SegmentDirection: we'd have the added value of enum and the flexibility of abstract classes. That's only if we want to give developers more flexibility that they probably need, in case they find a new typical case to implement.

    Exactly my thinking. Even though, I will need to also add getLatitudeDelta and some more SegmentDirections since we may also want to support calculating distances from one of the poles around the world to itself again.
    This would call for strictlySouthward and strictlyNorthward as well - leading to two additional parameters for the distance function (one for lat, one for lng).

  12. 4 remaining items

  13. ThexXTURBOXx commented on May 7, 2026

    @ThexXTURBOXx
    ContributorAuthor

    Addressed all the comments in 1.0.0-dev.2.
    Please let me know if there is anything else I can do for flutter_map! :)

  14. monsieurtanuki commented on May 11, 2026

    @monsieurtanuki
    Contributor

    Addressed all the comments in 1.0.0-dev.2.

    Cool!

    Please let me know if there is anything else I can do for flutter_map! :)

    We'll let you know.

    I guess the first idea that comes to mind is: get me the closest LatLng with those latitude and longitude, according to the SegmentDirection. But that would mean longitude beyond -180 and 180. Maybe here we could return -1, 0, 1 as previous, current, next world. getClosestLongitudeWorld? To be investigated.

  15. JaffaKetchup commented on May 11, 2026

    @JaffaKetchup
    Member

    Hey, sorry I haven't managed to keep up with this. I'll try and give it a read over and get up to speed tonight. But thanks guys for working on this :)

  16. JaffaKetchup commented on May 16, 2026

    @JaffaKetchup
    Member

    Ok, I've finally found some time to look through it all, and I do like the way it's going. I think it all makes sense.

    The only thing I would nitpick is the LongitudeDirection.lazy name. In my head at least, lazy inferred shortest path. I do get why you've called it that, but I think another name would work best, maybe "greenwich" as suggested. (If this was flutter_map territory, I guess this is basically "single/one world"? but yeah I don't think that's a good name for here.)

    (Also the API reference for the prereleases seems to have split up all the libraries so maybe there's some weird exporting going on somewhere.)

    What's happening with Path? From what I can see it hasn't been changed at all, and I would say that it needs a serious overhaul. .distance doesn't seem to have an externally settable calculator; the idea of .center is really simplistic; it looks like it's trying to be a list or iterable without implementing either of those interfaces; and now of course the concepts (distance, area) are more complicated because of all this. I guess it's a similar problem to what our Polygon, Polyline, and LatLngBounds are trying to do recently all rolled into one?

    So is the answer to have a single path concept somewhere (maybe in latlong2) which takes care of all this, then flutter_map's job for Polygon and Polyline just become different ways to render paths? Is a path a list of latlngs: surely each 'join' between two latlngs needs to be described by a 'direction' (either implicitly or explicitly)? Trying to find the center of a path is something we have multiple ways of doing, including one that relies on 'dart_polylabel2': is this something that latlong2 wants to consider adding to path, or is it better to remove the concept of 'center' altogether?
    (Then we need to think about LatLngBounds because it's obviously got a different purpose, but many of the same issues. I don't think we could just start using paths in it's place. See also #2205.)
    Basically, where does the line go between FM and latlong2, and what's happening to 'path'?

    What do other mapping libraries do about all of this? geojson just says that things crossing the anti-meridian need to be split into separate geometries. Does it make sense to consider how we would parse geojson into latlong2 concepts and flutter_map concepts, and how we would generate geojson from those concepts?

    Maybe I've completely missed something with Path though, I'm on my phone and only looking at the pub.dev generated dartdoc.

  17. monsieurtanuki commented on May 23, 2026

    @monsieurtanuki
    Contributor

    @JaffaKetchup You're right, there's probably something to do with Path, and lists of LatLng in general: how (and where) do we add a point to a list? Probably with an optional LongitudeDirection parameter. Probably defaulting to a Distance that can be set in the constructor.

    That said, the new concept of LongitudeDirection looks bug-free, interesting enough, and definitely relevant for latlong2 users.
    I would create a release of latlong2 at this stage - modulo the lazy/greenwich settlement.

  18. ThexXTURBOXx commented on May 31, 2026

    @ThexXTURBOXx
    ContributorAuthor

    I am back from my vacation now - sorry for the delay!

    Ok, I've finally found some time to look through it all, and I do like the way it's going. I think it all makes sense.

    The only thing I would nitpick is the LongitudeDirection.lazy name. In my head at least, lazy inferred shortest path. I do get why you've called it that, but I think another name would work best, maybe "greenwich" as suggested. (If this was flutter_map territory, I guess this is basically "single/one world"? but yeah I don't think that's a good name for here.)

    I have changed this to greenwich. I agree that this makes more sense.

    (Also the API reference for the prereleases seems to have split up all the libraries so maybe there's some weird exporting going on somewhere.)

    Indeed. I have restructured the code into the standard lib/src setup, which solved this issue.

    What's happening with Path? From what I can see it hasn't been changed at all, and I would say that it needs a serious overhaul. .distance doesn't seem to have an externally settable calculator; the idea of .center is really simplistic; it looks like it's trying to be a list or iterable without implementing either of those interfaces; and now of course the concepts (distance, area) are more complicated because of all this. I guess it's a similar problem to what our Polygon, Polyline, and LatLngBounds are trying to do recently all rolled into one?

    I do agree! I noticed that Path is overall just a "bad" (?) wrapper around List<LatLng>. It is bad because it misses quite a lot of functions from the List class, which makes it hard to work with.
    After fiddling around with it for a bit, I believe that the concept of a Path can be made much easier and nicer using the following code:

    typedef Path = List<LatLng>;
    
    extension PathExtensions on Path { // TODO: List<LatLng> or Path here? Both work...
      // Put all the functions from the original Path class here
    }

    This way, the new Path immediately has all the functions from the original List class - just with a few more.
    Have not noticed any problems with this approach yet. I have not tested with all the functions though, yet.
    Big disadvantage I can see in this approach, though: We cannot save the distance and latLngFactory in the object and they would probably need to be specified in every method call, which is bad.

    Apart from this problem: Yes, .distance and .center also have both their own disadvantages and shortcomings, which I will try to solve before releasing 1.0.0 as stability for the future is of utmost importance for me and the current interface cannot stay stable for that long.

    So is the answer to have a single path concept somewhere (maybe in latlong2) which takes care of all this, then flutter_map's job for Polygon and Polyline just become different ways to render paths? Is a path a list of latlngs: surely each 'join' between two latlngs needs to be described by a 'direction' (either implicitly or explicitly)? Trying to find the center of a path is something we have multiple ways of doing, including one that relies on 'dart_polylabel2': is this something that latlong2 wants to consider adding to path, or is it better to remove the concept of 'center' altogether? (Then we need to think about LatLngBounds because it's obviously got a different purpose, but many of the same issues. I don't think we could just start using paths in it's place. See also #2205.) Basically, where does the line go between FM and latlong2, and what's happening to 'path'?

    What do other mapping libraries do about all of this? geojson just says that things crossing the anti-meridian need to be split into separate geometries. Does it make sense to consider how we would parse geojson into latlong2 concepts and flutter_map concepts, and how we would generate geojson from those concepts?

    Maybe I've completely missed something with Path though, I'm on my phone and only looking at the pub.dev generated dartdoc.

    I don't think you missed something. It is just exactly like you described: not the easiest task to solve.
    I am also quite conflicted on deciding which features should go into latlong2, which should go into dependents and which should go into dart_polylabel2...

  19. JaffaKetchup commented on May 31, 2026

    @JaffaKetchup
    Member

    Hey no worries, hope you had a good holiday!

    I think representing path as a list of latlngs is too simplistic if we want to specify which direction to take between each pair. If we don't then what's the alternative (and what's the point of direction if not used in a case like this)?

    I've got some ideas of how this could be represented using a custom Iterable and Iterator implementation along with maybe a type extension if we really want to lock down the API. But I'm a little worried of over engineering.

    Personally I would say distance can remain on path, but center should be removed altogether (especially because from what I understand it might not be the geographical center (respecting the sphere etc)?).

  20. monsieurtanuki commented on Jun 1, 2026

    @monsieurtanuki
    Contributor

    A fair description of a path would be an origin and a list of vectors. May prove problematic regarding numerous rounding side-effect.
    Or, if we're more accurate, an origin and a list of (LatLng, LongitudeDirection)s, or (LatLng, worldNumber)s.
    But that would take more space.

    I guess it'd be reasonable to maintain a couple of Paths:

    • the lazy "I don't change worlds anyway" Path, that most developers would use, something like LocalPath, OneWorldPath, GreenwichPath, similar to what we currently have
    • a 100% accurate MagellanPath that handles paths crossing the -180 / 180 boundaries, with a worldNumber attached to each LatLng
    • both paths would somehow returns the nth LatLng and the nth world number (always 0 for GreenwichPath)
  21. JaffaKetchup commented on Jun 1, 2026

    @JaffaKetchup
    Member

    Agreed, that makes sense. I guess we would need to use world numbers right? Because if the next point skips a world then a direction won't accurately describe it.

    Should be pretty simple to make a class which holds an origin and a list of point-worldnumber pairs. Make it Iterable then the Iterator can return something like (LatLng, int). Or we could return the vector if we think that's something we need more often. When you say rounding side effects are you referring to floating point stuff and ending up with drift from what was really intended?
    And provide similar methods to list manipulation like add and remove (with optional parameters for the world numbers). If we wanted to have both variants then they both inherit from a super class, we just save memory when using a single world.

    It does make consuming it more complicated of course but at least it's accurate.

    An alternative would be to spit out potentially multiple points when iterated? Rather than getting a world number out it would fake an additional point on the anti-meridian? Although then idk if that's easy to calculate ('straight line' or 'great circle'?) and it doesn't really fit with what we've been doing. And maybe it causes problems later? But it does simplify the consumption a bit. And if two same anti-meridian points are next to each other, does that infer a world apart or 'nil move'? Similar to what geojson does.

  22. monsieurtanuki commented on Jun 1, 2026

    @monsieurtanuki
    Contributor

    When you say rounding side effects are you referring to floating point stuff and ending up with drift from what was really intended?

    Yes

  23. S-Man42 commented on Jun 22, 2026

    @S-Man42

    Would it make sense to also maintain "strictlyEastward" and "strictlyWestward", in case you want to compute the size of equator with [(0,0),(0,0))?

    Hi, I currently read this thread and stumbled over this one. I really don't know if it still relevant but I wondered if there's even a use case for "strictlyNorthward" and "strictlySouthward". In that case going over the poles would be the shortest path. So, there are always different ways and I believe there may be even more edge cases to look at. So maybe an initial azimuth could make more sense than defining a lot of edgy enum values or similar...

    Just what came to mind...

  24. JaffaKetchup commented on Oct 8, 2026

    @JaffaKetchup
    Member

    Do we want to move #2205 forward without this? It is kinda treading a little into this territory (behaviour of latlong bounds across the anti-meridian) but not sure, think it's probably OK?

  25. ThexXTURBOXx commented on Oct 12, 2026

    @ThexXTURBOXx
    ContributorAuthor

    Still thinking about how to organize latlong2 exactly. I think, it's okay to move forward without touching latlong2 for now.
    A stable 1.0.0 release will take a considerable amount of time anyway, so I'm not sure if it would be worth waiting for that...
    The stable 1.0.0 release would most likely count as breaking either way, so flutter_map would need to increase its major version as well

  26. ThexXTURBOXx commented on Nov 8, 2026

    @ThexXTURBOXx
    ContributorAuthor

    After some time, I finally had some time to think through most of the stuff, but I am not satisfied with the result(s), sadly.
    You can take a look at my unordered thoughts here: ThexXTURBOXx/dart-latlong@5772262
    Implementing the concept of multiple worlds means having to deal with "invalid" coordinates in some places, I believe. WorldLatLng tries to address this issue to some extent, but it is not entirely clear what should happen in some of the cases you might come across in production.
    I also added a preliminary implementation of LatLngBounds, which may be useful (because if I understood correctly, it may make sense to move this from flutter_map to latlong2, right?).
    However: what happens if we calculate the center of a LatLngBounds with a southWest object that is in world 0 (let's say, a normal LatLng object) and a northEast object in world 3 (i.e., a WorldLatLng instance)? Should the center be a LatLng object with invalid coordinates or a WorldLatLng object with proper worldNumber? I would think the latter - but does the user really expect this or may they expect configurable behaviour or something else?
    Using the conversion helper functions in both LatLng and WorldLatLng, one can convert between the two views, so one could force a specific view in the end by calling the respective helper function.
    But all of this gets rather complicated the more edge cases you think about... I am a bit overwhelmed by all the possibilities currently and don't know whether my current chain of thought may lead to a dead end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    dependencies[CI] Pull requests that update a dependency filegeneral[DEPRECATED] This issue is better suited for Discussions or Discord

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions