diff --git a/src/GpsPoint.php b/src/GpsPoint.php new file mode 100644 index 000000000..1facd5320 --- /dev/null +++ b/src/GpsPoint.php @@ -0,0 +1,131 @@ + '%value% is out of Bounds.', + 'gpsPointConvertError' => '%value% can not converted into a Decimal Degree Value.', + 'gpsPointIncompleteCoordinate' => '%value% did not provided a complete Coordinate', + ]; + + /** + * Returns true if and only if $value meets the validation requirements + * + * If $value fails validation, then this method returns false, and + * getMessages() will return an array of messages that explain why the + * validation failed. + * + * @param mixed $value + * @return bool + * @throws Exception\RuntimeException If validation of $value is impossible + */ + public function isValid($value) + { + if (strpos($value, ',') === false) { + $this->error(GpsPoint::INCOMPLETE_COORDINATE, $value); + return false; + } + + list($lat, $long) = explode(',', $value); + + if ($this->isValidCoordinate($lat, 90.0000) && $this->isValidCoordinate($long, 180.000)) { + return true; + } + + return false; + } + + /** + * @param string $value + * @param $maxBoundary + * @return bool + */ + private function isValidCoordinate($value, $maxBoundary) + { + $this->value = $value; + + $value = $this->removeWhiteSpace($value); + if ($this->isDMSValue($value)) { + $value = $this->convertValue($value); + } else { + $value = $this->removeDegreeSign($value); + } + + if ($value === false || $value === null) { + $this->error(self::CONVERT_ERROR); + return false; + } + + $doubleLatitude = (double)$value; + + if ($doubleLatitude <= $maxBoundary && $doubleLatitude >= $maxBoundary * -1) { + return true; + } + + $this->error(self::OUT_OF_BOUNDS); + return false; + } + + /** + * Determines if the give value is a Degrees Minutes Second Definition + * + * @param $value + * @return bool + */ + private function isDMSValue($value) + { + return preg_match('/([°\'"]+[NESW])/', $value) > 0; + } + + + /** + * @param string $value + * @return bool|string + */ + private function convertValue($value) + { + $matches = []; + $result = preg_match_all('/(\d{1,3})°(\d{1,2})\'(\d{1,2}[\.\d]{0,6})"[NESW]/i', $value, $matches); + + if ($result === false || $result === 0) { + return false; + } + + return $matches[1][0] + $matches[2][0]/60 + ((double)$matches[3][0])/3600; + } + + /** + * @param string $value + * @return string + */ + private function removeWhiteSpace($value) + { + return preg_replace('/\s/', '', $value); + } + + /** + * @param string $value + * @return string + */ + private function removeDegreeSign($value) + { + return str_replace('°', '', $value); + } +} diff --git a/test/GPSPointTest.php b/test/GPSPointTest.php new file mode 100644 index 000000000..a149efa91 --- /dev/null +++ b/test/GPSPointTest.php @@ -0,0 +1,83 @@ +validator = new GpsPoint(); + } + + + /** + * @dataProvider basicDataProvider + * @covers \Zend\Validator\GPSPoint::isValid + */ + public function testBasic($gpsPoint) + { + $this->assertTrue($this->validator->isValid($gpsPoint)); + } + + /** + * @covers \Zend\Validator\GPSPoint::isValid + */ + public function testBoundariesAreRespected() + { + $this->assertFalse($this->validator->isValid('181.8897,-77.0089')); + $this->assertFalse($this->validator->isValid('38.8897,-181.0089')); + $this->assertFalse($this->validator->isValid('-181.8897,-77.0089')); + $this->assertFalse($this->validator->isValid('38.8897,181.0089')); + } + + /** + * @covers \Zend\Validator\GPSPoint::isValid + * @dataProvider ErrorMessageTestValues + */ + public function testErrorsSetOnOccur($value, $messageKey, $messageValue) + { + $this->assertFalse($this->validator->isValid($value)); + $messages = $this->validator->getMessages(); + $this->assertArrayHasKey($messageKey, $messages); + $this->assertContains($messageValue, $messages[$messageKey]); + } + + public function basicDataProvider() + { + return [ + ['38° 53\' 23" N, 77° 00\' 32" W'], + ['15° 22\' 20.137" S, 35° 35\' 14.686" E'], + ['65° 4\' 36.434" N,-22.728867530822754'], + ['38.8897°, -77.0089°'], + ['38.8897,-77.0089'] + ]; + } + + public function ErrorMessageTestValues() + { + return [ + ['63 47 24.691 N, 18 2 54.363 W', GpsPoint::OUT_OF_BOUNDS, '63 47 24.691 N'], + ['° \' " N,° \' " E', GpsPoint::CONVERT_ERROR, '° \' " N'], + ['° \' " N', GpsPoint::INCOMPLETE_COORDINATE, '° \' " N'], + ]; + } +}