MatanYadaev / laravel-eloquent-spatial

Laravel Eloquent spatial package.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Factory is incorrectly swapping lat/long when creating point

AlexGodbehere opened this issue · comments

I have a location column in my table that is a PostGIS geometry type with SRID 4326 and value 0101000020E61000005C8FC2F528544B4094FB1D8A02DD18C0. It is the location of Belfast Airport. DataGrip confirms this:

image

In the createFromGeometry() method of Factory the $geoPHPGeometry object is created, which looks like this:

image

But then, for whatever reason, Factory is swapping the two coordinates with return new Point($geometry->coords[1], $geometry->coords[0], $srid);, which results in the incorrect point:

> Airport::whereIdent('EGAA')->sole()->location
[!] Aliasing 'Airport' to 'App\Domain\Airports\Models\Airport' for this Tinker session.
= MatanYadaev\EloquentSpatial\Objects\Point {#794
    +srid: 4326,
    +latitude: -6.21583,
    +longitude: 54.6575,
  }

Have I discovered a bug or am I missing something?

I'll check it out later. Meanwhile, can you please fork this repo, write a failing test that reproduces the bug? It will help me a lot.

First @MatanYadaev this is a nice package and thank you for this.
@AlexGodbehere yes I can confirm it is a bug and this is a serious problem.

@MatanYadaev please don't get me wrong, but I need to pinpoint this fundamental problem and potential solution:

Industry standard is lat long order.
Order of these lat|long values should not be changed at any case, cause it is crucial to work.

You correctly have this in class Point construct already:
public function __construct(float $latitude, float $longitude, int|Srid $srid = 0)

but then suddenly this order is changed in Point class with these functions:

  1. getWktData():
public function getWktData(): string
{
    return "{$this->longitude} {$this->latitude}";
}
  1. getCoordinates():
public function getCoordinates(): array
    {
        return [
            $this->longitude,
            $this->latitude,
        ];
    }

It should be the opposite i.e.:

  1. getWktData():
public function getWktData(): string
{
    return " {$this->latitude} {$this->longitude}";
}
  1. getCoordinates():
public function getCoordinates(): array
    {
        return [
            $this->latitude,
            $this->longitude,
        ];
    }

I'm writing this based on Point class in the latest release => 4.2.1
I have not analyzed the whole lib, but it seems that these small changes should fix it all.
I know it is a braking change, but it is better to fix it now then develop code further on wrong basics.

@MatanYadaev @AlexGodbehere a small update,
I've looked at factory code, and unfortunately my proposed small fix would not solve all problems.

protected static function createFromGeometry(geoPHPGeometry $geometry): Geometry
    {
        $srid = is_int($geometry->getSRID()) ? $geometry->getSRID() : 0;

        if ($geometry instanceof geoPHPPoint) {
            if ($geometry->coords[0] === null || $geometry->coords[1] === null) {
                throw new InvalidArgumentException('Invalid spatial value');
            }

            return new Point($geometry->coords[1], $geometry->coords[0], $srid);
        }

Here values were mixed again:
return new Point($geometry->coords[1], $geometry->coords[0], $srid);
Normally it should be the other way around:
return new Point($geometry->coords[0], $geometry->coords[1], $srid);

Therefore whole package needs to be reviewed again to ensure latitude is always in the first place and longitude second in all classes, methods and variables.

If any doubts please check out point implementation on biggest map providers -> Google Maps, Bing Maps, Apple Maps and others...

Hi @chilio, thanks for the detailed investigation. I'm sorry but I'm not available to investigate it at the moment, will have time in a few days.
But I just wanna say that GeoJSON uses longitude-first approach, and PostGIS, and more. I am aware of map services uses latitude-first approach. I just not sure yet what you say is a serious problem, or a problem at all. Anyway, I need to investigate it to confirm it.

Other then that, I will appreciate if you could push a failing test that will help me reproduce the problem more easily.

Thanks for diving into this @chilio.

I think I understand where the issue is here. My location column did in fact have the columns stored the wrong way around. I think DataGrip was being clever somehow and flipping them for me when displaying the map.

Running the following on the columns sovled this. The map still shows the correct location and this package is happy.

UPDATE airports
SET location = ST_FlipCoordinates(location);

Thanks for this @MatanYadaev - it's a great package.

TL;DR; My coordinates were stored in the database in the wrong order.

Hi @chilio, thanks for the detailed investigation. I'm sorry but I'm not available to investigate it at the moment, will have time in a few days. But I just wanna say that GeoJSON uses longitude-first approach, and PostGIS, and more. I am aware of map services uses latitude-first approach. I just not sure yet what you say is a serious problem, or a problem at all. Anyway, I need to investigate it to confirm it.

Hi @MatanYadaev, you were totally right about GeoJson, WKT, PostGis and others.
So please treat my recommendations as false positives.
I was wrong assuming, that the order of these vars should be consistent at all times,
Therefore even more kudos to your package. handling all these cases.

BTW:
On the other hand, It would be nice, if there was some standard for geo points, meaning order of these vars, then nobody would not have any doubts about it.

Thanks again for your package and sorry for being misleading here. Case closed again.