Coordinate::distanceFrom()   B
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 24
ccs 15
cts 15
cp 1
rs 8.9713
c 0
b 0
f 0
cc 3
eloc 14
nc 4
nop 3
crap 3
1
<?php
2
3
namespace ValueObjects\Geography;
4
5
use League\Geotools\Convert\Convert;
6
use League\Geotools\Distance\Distance;
7
use ValueObjects\Number\Real;
8
use ValueObjects\StringLiteral\StringLiteral;
9
use ValueObjects\Util\Util;
10
use ValueObjects\ValueObjectInterface;
11
use League\Geotools\Coordinate\Coordinate as BaseCoordinate;
12
use League\Geotools\Coordinate\Ellipsoid as BaseEllipsoid;
13
14
class Coordinate implements ValueObjectInterface
15
{
16
    /** @var Latitude */
17
    protected $latitude;
18
19
    /** @var Longitude */
20
    protected $longitude;
21
22
    /** @var Ellipsoid */
23
    protected $ellipsoid;
24
25
    /**
26
     * Returns a new Coordinate object from native PHP arguments
27
     *
28
     * @return self
29
     * @throws \BadMethodCallException
30
     */
31 2
    public static function fromNative()
32
    {
33 2
        $args = \func_get_args();
34
35 2
        if (\count($args) < 2 || \count($args) > 3) {
36 1
            throw new \BadMethodCallException('You must provide 2 to 3 arguments: 1) latitude, 2) longitude, 3) valid ellipsoid type (optional)');
37
        }
38
39 1
        $coordinate = new BaseCoordinate(array($args[0], $args[1]));
40 1
        $latitude  = Latitude::fromNative($coordinate->getLatitude());
41 1
        $longitude = Longitude::fromNative($coordinate->getLongitude());
42
43 1
        $nativeEllipsoid = isset($args[2]) ? $args[2] : null;
44 1
        $ellipsoid = Ellipsoid::fromNative($nativeEllipsoid);
45
46 1
        return new static($latitude, $longitude, $ellipsoid);
47
    }
48
49
    /**
50
     * Returns a new Coordinate object
51
     *
52
     * @param Latitude  $latitude
53
     * @param Longitude $longitude
54
     * @param Ellipsoid $ellipsoid
55
     */
56 9
    public function __construct(Latitude $latitude, Longitude $longitude, Ellipsoid $ellipsoid = null)
57
    {
58 9
        if (null === $ellipsoid) {
59 9
            $ellipsoid = Ellipsoid::WGS84();
60 9
        }
61
62 9
        $this->latitude   = $latitude;
63 9
        $this->longitude  = $longitude;
64 9
        $this->ellipsoid  = $ellipsoid;
65 9
    }
66
67
    /**
68
     * Tells whether tow Coordinate objects are equal
69
     *
70
     * @param  ValueObjectInterface $coordinate
71
     * @return bool
72
     */
73 2
    public function sameValueAs(ValueObjectInterface $coordinate)
74
    {
75 2
        if (false === Util::classEquals($this, $coordinate)) {
76 1
            return false;
77
        }
78
79 2
        return $this->getLatitude()->sameValueAs($coordinate->getLatitude())   &&
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface ValueObjects\ValueObjectInterface as the method getLatitude() does only exist in the following implementations of said interface: ValueObjects\Geography\Coordinate.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
80 2
               $this->getLongitude()->sameValueAs($coordinate->getLongitude()) &&
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface ValueObjects\ValueObjectInterface as the method getLongitude() does only exist in the following implementations of said interface: ValueObjects\Geography\Coordinate.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
81 2
               $this->getEllipsoid()->sameValueAs($coordinate->getEllipsoid())
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface ValueObjects\ValueObjectInterface as the method getEllipsoid() does only exist in the following implementations of said interface: ValueObjects\Geography\Coordinate.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
82 2
        ;
83
    }
84
85
    /**
86
     * Returns latitude
87
     *
88
     * @return Latitude
89
     */
90 7
    public function getLatitude()
91
    {
92 7
        return clone $this->latitude;
93
    }
94
95
    /**
96
     * Returns longitude
97
     *
98
     * @return Longitude
99
     */
100 7
    public function getLongitude()
101
    {
102 7
        return clone $this->longitude;
103
    }
104
105
    /**
106
     * Returns ellipsoid
107
     *
108
     * @return Ellipsoid
109
     */
110 7
    public function getEllipsoid()
111
    {
112 7
        return $this->ellipsoid;
113
    }
114
115
    /**
116
     * Returns a degrees/minutes/seconds representation of the coordinate
117
     *
118
     * @return StringLiteral
119
     */
120 1
    public function toDegreesMinutesSeconds()
121
    {
122 1
        $coordinate = static::getBaseCoordinate($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<ValueObjects\Geography\Coordinate>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
123 1
        $convert    = new Convert($coordinate);
124 1
        $dms        = $convert->toDegreesMinutesSeconds();
125
126 1
        return new StringLiteral($dms);
127
    }
128
129
    /**
130
     * Returns a decimal minutes representation of the coordinate
131
     *
132
     * @return StringLiteral
133
     */
134 1
    public function toDecimalMinutes()
135
    {
136 1
        $coordinate = static::getBaseCoordinate($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<ValueObjects\Geography\Coordinate>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
137 1
        $convert    = new Convert($coordinate);
138 1
        $dm         = $convert->toDecimalMinutes();
139
140 1
        return new StringLiteral($dm);
141
    }
142
143
    /**
144
     * Returns a Universal Transverse Mercator projection representation of the coordinate in meters
145
     *
146
     * @return StringLiteral
147
     */
148 1
    public function toUniversalTransverseMercator()
149
    {
150 1
        $coordinate = static::getBaseCoordinate($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<ValueObjects\Geography\Coordinate>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
151 1
        $convert    = new Convert($coordinate);
152 1
        $utm        = $convert->toUniversalTransverseMercator();
153
154 1
        return new StringLiteral($utm);
155
    }
156
157
    /**
158
     * Calculates the distance between two Coordinate objects
159
     *
160
     * @param  Coordinate      $coordinate
161
     * @param  DistanceUnit    $unit
162
     * @param  DistanceFormula $formula
163
     * @return Real
164
     */
165 1
    public function distanceFrom(Coordinate $coordinate, DistanceUnit $unit = null, DistanceFormula $formula = null)
166
    {
167 1
        if (null === $unit) {
168 1
            $unit = DistanceUnit::METER();
169 1
        }
170
171 1
        if (null === $formula) {
172 1
            $formula = DistanceFormula::FLAT();
173 1
        }
174
175 1
        $baseThis       = static::getBaseCoordinate($this);
0 ignored issues
show
Documentation introduced by
$this is of type this<ValueObjects\Geography\Coordinate>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
176 1
        $baseCoordinate = static::getBaseCoordinate($coordinate);
0 ignored issues
show
Documentation introduced by
$coordinate is of type object<ValueObjects\Geography\Coordinate>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
177
178 1
        $distance = new Distance();
179
        $distance
180 1
            ->setFrom($baseThis)
181 1
            ->setTo($baseCoordinate)
182 1
            ->in($unit->toNative())
183
        ;
184
185 1
        $value = \call_user_func(array($distance, $formula->toNative()));
186
187 1
        return new Real($value);
188
    }
189
190
    /**
191
     * Returns a native string version of the Coordiantes object in format "$latitude,$longitude"
192
     *
193
     * @return string
194
     */
195 1
    public function __toString()
196
    {
197 1
        return \sprintf('%F,%F', $this->getLatitude()->toNative(), $this->getLongitude()->toNative());
198
    }
199
200
    /**
201
     * Returns the underlying Coordinate object
202
     *
203
     * @param  self           $coordinate
204
     * @return BaseCoordinate
205
     */
206 4
    protected static function getBaseCoordinate(self $coordinate)
207
    {
208 4
        $latitude   = $coordinate->getLatitude()->toNative();
209 4
        $longitude  = $coordinate->getLongitude()->toNative();
210 4
        $ellipsoid  = BaseEllipsoid::createFromName($coordinate->getEllipsoid()->toNative());
211 4
        $coordinate = new BaseCoordinate(array($latitude, $longitude), $ellipsoid);
212
213 4
        return $coordinate;
214
    }
215
}
216