Ejemplo n.º 1
0
 public function testConstructorShouldAcceptLocalizedFloatsAsArguments()
 {
     $currentLocale = setlocale(LC_NUMERIC, '0');
     setlocale(LC_NUMERIC, 'de_DE.utf8', 'de_DE@euro', 'de_DE', 'deu_deu');
     $latitude = floatval('1.1234');
     $longitude = floatval('2.5678');
     $LatLng = new LatLng($latitude, $longitude);
     $this->assertSame(1.1234, $LatLng->getLatitude());
     $this->assertSame(2.5678, $LatLng->getLongitude());
     setlocale(LC_NUMERIC, $currentLocale);
 }
Ejemplo n.º 2
0
 public final function exportDune()
 {
     if (is_null($this->timeSeries)) {
         throw new InvalidOperationException('Total water level data has not been loaded. Maximum level cannot be determined.');
     }
     Site::setDatabaseCredentials(self::$databaseCredentials);
     $outputArray = array('dune' => array('id' => $this->duneId, 'selectedSiteId' => $this->siteId, 'siteName' => Site::getNameById($this->siteId), 'latitude' => $this->position->getLatitude(), 'longitude' => $this->position->getLongitude(), 'toe' => $this->toeHeight / 1000, 'crest' => $this->crestHeight / 1000, 'impactCode' => NULL), 'meta' => array('yAxisMax' => $this->crestHeight / 1000, 'yAxisMin' => 0), 'timeSeries' => NULL);
     if ($this->hasTimeSeries()) {
         foreach ($this->timeSeries as $dataPoint) {
             if ($dataPoint->getTwl() > $this->crestHeight) {
                 $impactCode = 2;
             } elseif ($dataPoint->getTwl() > $this->toeHeight) {
                 $impactCode = 1;
             } else {
                 $impactCode = 0;
             }
             $outputArray['timeSeries'][] = array('date' => $dataPoint->getDate(), 'time' => $dataPoint->getTime(), 'timeStamp' => $dataPoint->getTimestamp() * 1000, 'twl' => $dataPoint->getTwl() / 1000, 'twl05' => $dataPoint->getTwl05() / 1000, 'twl95' => $dataPoint->getTwl95() / 1000, 'tideWind' => $dataPoint->getTideWindLevel() / 1000, 'im' => $impactCode);
             if ($dataPoint->getTwl95() / 1000 > $outputArray['meta']['yAxisMax']) {
                 $outputArray['meta']['yAxisMax'] = $dataPoint->getTwl95() / 1000;
             }
             if ($dataPoint->getTideWindLevel() / 1000 < $outputArray['meta']['yAxisMin']) {
                 $outputArray['meta']['yAxisMin'] = $dataPoint->getTideWindLevel() / 1000;
             }
             if ($dataPoint->getTwl05() / 1000 < $outputArray['meta']['yAxisMin']) {
                 $outputArray['meta']['yAxisMin'] = $dataPoint->getTwl95() / 1000;
             }
             if (is_null($outputArray['dune']['impactCode']) || $impactCode > $outputArray['dune']['impactCode']) {
                 $outputArray['dune']['impactCode'] = $impactCode;
             }
         }
     }
     $yMax = ceil($outputArray['meta']['yAxisMax']);
     if ($yMax - $outputArray['meta']['yAxisMax'] < 0.5) {
         $yMax += 0.5;
     }
     $outputArray['meta']['yAxisMax'] = $yMax;
     $yMin = floor($outputArray['meta']['yAxisMin']);
     if ($outputArray['meta']['yAxisMax'] + $yMin < 0.5) {
         $yMin -= 0.5;
     }
     $outputArray['meta']['yAxisMin'] = $yMin;
     return $outputArray;
 }
Ejemplo n.º 3
0
 private function transformBounds($input, $distanceInMeters)
 {
     $bounds = Utils::castToBounds($input);
     $latSW = $bounds->getSouthWest()->getLatitude();
     $lngSW = $bounds->getSouthWest()->getLongitude();
     $latNE = $bounds->getNorthEast()->getLatitude();
     $lngNE = $bounds->getNorthEast()->getLongitude();
     $latlngSW = new LatLng($this->latDistance($latSW, $distanceInMeters), $this->lngDistance($latSW, $lngSW, $distanceInMeters));
     $latlngNE = new LatLng($this->latDistance($latNE, -$distanceInMeters), $this->lngDistance($latNE, $lngNE, -$distanceInMeters));
     // Check if we're shrinking too much
     if ($latlngSW->getLatitude() > $latlngNE->getLatitude()) {
         $center = $bounds->getCenter();
         return new Bounds($center, $center);
     }
     return new Bounds($latlngSW, $latlngNE);
 }
Ejemplo n.º 4
0
 /**
  * Check if given object in in the bounds.
  *
  * @param LatLng|LatLngBounds $object The given object.
  *
  * @return bool
  * @throws \RuntimeException If LatLngBounds is checked. Not implemented yet.
  */
 public function contains($object)
 {
     if ($object instanceof LatLng) {
         $lat = $object->getLatitude();
         $lng = $object->getLongitude();
         if ($this->getWest() > $lng || $this->getEast() < $lng) {
             return false;
         }
         return $this->getSouth() <= $lat && $this->getNorth() >= $lat;
     } elseif ($object instanceof LatLngBounds) {
         throw new \RuntimeException('LatLngBounds checking not implemented so far');
     }
     return false;
 }
Ejemplo n.º 5
0
 /**
  * @param  LatLng $latLng
  * @return Bounds
  */
 public function extend(LatLng $latLng)
 {
     $newSouth = min($this->southWest->getLatitude(), $latLng->getLatitude());
     $newNorth = max($this->northEast->getLatitude(), $latLng->getLatitude());
     $newWest = $this->southWest->getLongitude();
     $newEast = $this->northEast->getLongitude();
     if (!$this->containsLng($latLng->getLongitude())) {
         // try extending east and try extending west, and use the one that
         // has the smaller longitudinal span
         $extendEastLngSpan = $this->lngSpan($newWest, $latLng->getLongitude());
         $extendWestLngSpan = $this->lngSpan($latLng->getLongitude(), $newEast);
         if ($extendEastLngSpan <= $extendWestLngSpan) {
             $newEast = $latLng->getLongitude();
         } else {
             $newWest = $latLng->getLongitude();
         }
     }
     return new self(new LatLng($newSouth, $newWest), new LatLng($newNorth, $newEast));
 }
Ejemplo n.º 6
0
    /**
     * Finds geographical features around the object's location
     *
     * Searches the geonames database for a specified number of features of the specified type within the specified
     * distance of the object's position ordered by either closest or furthest first. All search criteria may be
     * omitted to return an unrestrained (very large and not recommended) result set.
     *
     * @uses LatLng::convertMetersToDegreesLatitude
     * @uses LatLng::convertMetersToDegreesLongitude
     * @uses DatabaseHelper::getInstance
     * @uses DatabaseHelper::query
     * @uses DistantGeographicalFeature
     *
     * @param LatLng      $position            The co-ordinates from which the search should be based.
     * @param null|string $featureType         The type of feature to be searched for. Options are 'landmark', 'city'
     *                                         (all city sizes), 'majorCity', 'minorCity', NULL (all feature types).
     *                                         Default: NULL.
     * @param null|int    $searchRadius        The search radius in meters from the object's position.  Default = NULL.
     * @param null|int    $noOfResults         The number of results to return. Default = NULL.
     * @param string      $order               The order of the search results. Input options are 'asc' (closest first)
     *                                         or 'desc' furthest first. Default = 'asc.
     *
     * @return GeographicalFeature[]
     * @throws ArgumentOutOfRangeException For featureType, searchRadius, order, noOfResults.
     * @throws InvalidOperationException For executing the method without setting the database credentials at the
     *                                   class level.
     * @throws MyInvalidArgumentException For searchRadius, noOfResults.
     */
    public static final function findFeatures(LatLng $position, $featureType = NULL, $searchRadius = NULL, $noOfResults = NULL, $order = 'asc')
    {
        if (is_null(self::$databaseCredentials)) {
            throw new InvalidOperationException('Database credentials must be set at the class level to allow this action to take place.');
        }
        // Validates user input and builds the syntax relevant to the type of feature to search for.
        switch (strtolower($featureType)) {
            case 'landmark':
                $featureTypeSyntax = " feature_class != 'P' ";
                break;
            case 'city':
                $featureTypeSyntax = " feature_class = 'P' ";
                break;
            case 'majorcity':
                $featureTypeSyntax = " feature_class = 'P' AND population = 1 ";
                break;
            case 'minorcity':
                $featureTypeSyntax = " feature_class = 'P' AND population = 0 ";
                break;
            case NULL:
                $featureTypeSyntax = "";
                break;
            default:
                throw new ArgumentOutOfRangeException("INPUT: featureType. The supplied value type is invalid. Either 'landmark', 'city', 'majorCity',\n                    'minorCity', or NULL expected.", $featureType);
                break;
        }
        // Validates user input then builds the syntax to set a search radius.
        if (isset($searchRadius)) {
            if (!is_numeric($searchRadius)) {
                throw new MyInvalidArgumentException("INPUT: searchRadius. The supplied value type is invalid. A numeric value is expected.");
            }
            if ($searchRadius <= 0) {
                throw new ArgumentOutOfRangeException('INPUT: searchRadius. The supplied value is out of acceptable range. >0 expected.', $searchRadius);
            }
            $maxFeatureSearchDistanceInDegreesLatitude = LatLng::convertMetersToDegreesLatitude($searchRadius, $position->getLatitude());
            $maxFeatureSearchDistanceInDegreesLongitude = LatLng::convertMetersToDegreesLongitude($searchRadius, $position->getLatitude());
            $searchRadiusSyntax = <<<MYSQL

                    latitude
                        BETWEEN :latitude - {$maxFeatureSearchDistanceInDegreesLatitude}
                        AND :latitude + {$maxFeatureSearchDistanceInDegreesLatitude}
                AND
                    longitude
                        BETWEEN :longitude - {$maxFeatureSearchDistanceInDegreesLongitude}
                        AND :longitude + {$maxFeatureSearchDistanceInDegreesLongitude}

MYSQL;
            $havingSyntax = " HAVING distance < {$searchRadius} ";
        } else {
            $searchRadiusSyntax = '';
            $havingSyntax = '';
        }
        // Assembles the syntax for the WHERE clause combining feature and radius components built above.
        if ($featureTypeSyntax || $searchRadiusSyntax) {
            $whereSyntax = ' WHERE ';
            $whereSyntax .= $featureTypeSyntax;
            if ($featureTypeSyntax && $searchRadiusSyntax) {
                $whereSyntax .= ' AND ';
            }
            $whereSyntax .= $searchRadiusSyntax;
        } else {
            $whereSyntax = '';
        }
        // Validates user input and builds the syntax to set the result order
        switch (strtolower($order)) {
            case 'asc':
                $orderSyntax = ' ASC ';
                break;
            case 'desc':
                $orderSyntax = ' DESC ';
                break;
            default:
                throw new ArgumentOutOfRangeException("INPUT: order. The supplied value is invalid. Order can be either 'asc' or  'desc'", $order);
        }
        // Validates user input and builds the syntax to limit the number of results.
        if (isset($noOfResults)) {
            if (is_numeric($noOfResults)) {
                if ($noOfResults > 0) {
                    $limitSyntax = " LIMIT {$noOfResults}";
                } else {
                    throw new ArgumentOutOfRangeException("INPUT: noOfResults. The supplied value is invalid. Must be 1 or higher.", $noOfResults);
                }
            } else {
                throw new MyInvalidArgumentException("INPUT: noOfResults. The supplied value type is invalid. A numeric value is expected.");
            }
        } else {
            $limitSyntax = '';
        }
        // Assemble the final query from literal and dynamic components (built and assembled above based on user
        // criteria).
        $findFeaturesQuery = <<<MYSQL
            SELECT
                f.latitude,
                f.longitude,
                f.name AS name,
                f.feature_class AS featureClass,
                ft.feature_type AS featureType,
                c.county_name AS county,
                s.state_abbreviation AS state,
                ROUND(6378137 *
                    ACOS(COS(RADIANS(:latitude))
                    * COS(RADIANS(latitude))
                    * COS(RADIANS(longitude) - RADIANS(:longitude))
                    + SIN(RADIANS(:latitude))
                    * SIN(RADIANS(latitude)))) AS distance
            FROM
                features f
            LEFT JOIN feature_types ft
                ON f.feature_type_id = ft.feature_type_id
            LEFT JOIN counties c
                ON f.county_id = c.county_id AND f.state_id = c.state_id
            LEFT JOIN states s
                ON f.state_id = s.state_id
            {$whereSyntax}
            {$havingSyntax}
            ORDER BY
                distance {$orderSyntax}
            {$limitSyntax}
MYSQL;
        // The query parameters
        $findFeaturesParams = array('latitude' => $position->getLatitude(), 'longitude' => $position->getLongitude());
        // Connect to the database, run the query, build and return an array of DistantGeographicalFeature objects.
        $DBH = call_user_func_array('DatabaseHelper::getInstance', self::$databaseCredentials);
        $findFeaturesResult = DatabaseHelper::query($DBH, $findFeaturesQuery, $findFeaturesParams);
        $features = array();
        while ($feature = $findFeaturesResult->fetch(PDO::FETCH_ASSOC)) {
            try {
                if ($feature['featureClass'] == 'P') {
                    $city = $feature['name'];
                } else {
                    $featurePosition = new LatLng($feature['latitude'], $feature['longitude']);
                    $cityArray = self::findFeatures($geonamesCredentials, $featurePosition, 'majorCity', $searchRadius, 1, 'asc');
                    if ($cityArray) {
                        $city = $cityArray[0]->getCity();
                    } else {
                        $city = NULL;
                    }
                }
                $features[] = new GeographicalFeature(new LatLng($feature['latitude'], $feature['longitude']), $feature['name'], $feature['featureType'], $city, $feature['county'], $feature['state']);
            } catch (Exception $e) {
                continue;
            }
        }
        return $features;
    }
Ejemplo n.º 7
0
 /**
  * Calculate distance to another point using the haversine formula.
  *
  * Calculates the distance between two points in meters. Potential error of 0.5% as the formula uses a spherical
  * model ignoring the eccentricity of the Earth.
  *
  * @see  https://en.wikipedia.org/wiki/Haversine_formula
  *
  * @param LatLng $otherPosition The other position to which the distance should be calculated.
  *
  * @return int The distance between the two point in meters.
  */
 public final function distanceBetween(LatLng $otherPosition)
 {
     $point1LatitudeInRadians = deg2rad($this->latitude);
     $point1LongitudeInRadians = deg2rad($this->longitude);
     $point2LatitudeInRadians = deg2rad($otherPosition->getLatitude());
     $point2LongitudeInRadians = deg2rad($otherPosition->getLongitude());
     $meters = 6378137 * acos(cos($point1LatitudeInRadians) * cos($point2LatitudeInRadians) * cos($point2LongitudeInRadians - $point1LongitudeInRadians) + sin($point1LatitudeInRadians) * sin($point2LatitudeInRadians));
     return round($meters);
 }