Implementing a geographic coordinate class: equality comparison

我怕爱的太早我们不能终老 提交于 2019-12-05 00:52:39

A "location" with longitude/latitude to me falls quite nicely into the "immutable value" slot. The position itself doesn't change - if you change the latitude that is a different position. From there, it could be a struct; for float the struct would be the same size as an x64 reference anyway, so no real down side.

Re equality; if the position isn't quite the same, it isn't "equals", at least in the "key" perspective, so I'd be happy with == here. You could add a "is within (x) distance" method if it helped. Of course, great-arc geometry isn't exactly totally free either ;p

Thoughts though:

  • it should override bool Equals(object) as well as adding a bool Equals(GeoCoordinate)
  • it should override GetHashCode() and implement IEquatable<GeoCoordinate>
  • static operators are a nice-to-have optional extra
  • Equals is mainly used in dictionaries, so the guideline that you should compare floats only with an epsilon does not apply here. Do not attempt to put epsilon logic into Equals. It's the users job to do that as he needs. It's relatively easy to prove that the only hash function that's consistent with epsilon-comparisons it the constant hashfunction.

  • Your implementation has one problem though: It doesn't handle NaNs. You need to use Equals instead of == on the individual coordinates for your Equals method.

    public bool Equals(GeoCoordinate other)
    {
        if (other == null) {
            return false;
        }
    
        return this.latitude.Equals( other.latitude) && this.longitude.Equals(other.longitude);
    }
    
  • The "Don't compare using == but using epsilon" guideline is for the consuming code, not for the implementing code. So I'd implement a function that returns the distance between two geo-coordinates and tell the user to use this for his epsilon comparisons.

  • I'd definitely make it immutable (not sure if a struct or class). It has value semantics and thus should be immutable.
    I usually use something like Linq-to-Xml/Linq-to-Json for serialization, since this allows me to transform the representation between my in-memory model and the on-disk model.
    But you're right that many serializers don't support non default constructors. I regard that as a big flaw in those serializers, not as a flaw in my model. Some serializers simply access the private setters/fields, but personally I think that stinks.

I would just use long integers instead of floating-point numbers in the underlying model of latitude and longitude. A millisecond of degree is in any case less than two inches, which should be enough detail: store your coordinates in milliseconds and it's simpler, cleaner and bug-proof.

Depending on your use of the lat/lon struct, I would be concerned about using floats instead of doubles for lat/lon. For example, if you are doing real-time integration of lat/lon, then you will need double precision. This is because a degree is 1 nautical mile and depending on the time step of the integration, you are only moving a very small amount per time iteration.

For doing the equality test, I would use a simple distance formula based on a equirectangular approximation and decide that if the other point was within my decided tolerance (an inch or two), then declare them equal. The equirectangular approximation I would use is given here.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!