]> git.openstreetmap.org Git - nominatim.git/commitdiff
replace NearPoint with a more generic context object
authorSarah Hoffmann <lonvia@denofr.de>
Sun, 8 Oct 2017 19:23:31 +0000 (21:23 +0200)
committerSarah Hoffmann <lonvia@denofr.de>
Sun, 8 Oct 2017 19:23:31 +0000 (21:23 +0200)
The NearPoint is actually common to all SearchDescriptions
and there is other context data as well. like viewbox, that
needs to be available to the search object but is common.

lib/Geocode.php
lib/NearPoint.php [deleted file]
lib/ReverseGeocode.php
lib/SearchContext.php [new file with mode: 0644]
lib/SearchDescription.php
lib/lib.php

index 58bc3bfc1a2cf64d2528a7b5b5511d3dcfa27c61..a6baa96be830ef87560083a1eb4e595f80198215 100644 (file)
@@ -2,10 +2,10 @@
 
 namespace Nominatim;
 
-require_once(CONST_BasePath.'/lib/NearPoint.php');
 require_once(CONST_BasePath.'/lib/PlaceLookup.php');
 require_once(CONST_BasePath.'/lib/ReverseGeocode.php');
 require_once(CONST_BasePath.'/lib/SearchDescription.php');
+require_once(CONST_BasePath.'/lib/SearchContext.php');
 
 class Geocode
 {
@@ -888,6 +888,8 @@ class Geocode
     {
         if (!$this->sQuery && !$this->aStructuredQuery) return array();
 
+        $oCtx = new SearchContext();
+
         $sNormQuery = $this->normTerm($this->sQuery);
         $sLanguagePrefArraySQL = getArraySQL(
             array_map("getDBQuoted", $this->aLangPrefOrder)
@@ -926,20 +928,12 @@ class Geocode
         }
 
         // Do we have anything that looks like a lat/lon pair?
-        $oNearPoint = false;
-        if ($aLooksLike = NearPoint::extractFromQuery($sQuery)) {
-            $oNearPoint = $aLooksLike['pt'];
-            $sQuery = $aLooksLike['query'];
-        }
+        $sQuery = $oCtx->setNearPointFromQuery($sQuery);
 
         $aSearchResults = array();
         if ($sQuery || $this->aStructuredQuery) {
             // Start with a single blank search
-            $aSearches = array(new SearchDescription());
-
-            if ($oNearPoint) {
-                $aSearches[0]->setNear($oNearPoint);
-            }
+            $aSearches = array(new SearchDescription($oCtx));
 
             if ($sQuery) {
                 $sQuery = $aSearches[0]->extractKeyValuePairs($sQuery);
@@ -1166,7 +1160,7 @@ class Geocode
                         }
                     } elseif (!$oSearch->isNamedSearch()) {
                         // looking for a POI in a geographic area
-                        if (!$bBoundingBoxSearch && !$oSearch->isNearSearch()) {
+                        if (!$bBoundingBoxSearch && !$oCtx->hasNearPoint()) {
                             continue;
                         }
 
@@ -1319,11 +1313,7 @@ class Geocode
             $oReverse = new ReverseGeocode($this->oDB);
             $oReverse->setZoom(18);
 
-            $aLookup = $oReverse->lookup(
-                $oNearPoint->lat(),
-                $oNearPoint->lon(),
-                false
-            );
+            $aLookup = $oReverse->lookupPoint($oCtx->sqlNear, false);
 
             if (CONST_Debug) var_dump("Reverse search", $aLookup);
 
diff --git a/lib/NearPoint.php b/lib/NearPoint.php
deleted file mode 100644 (file)
index 30845b7..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-<?php
-
-namespace Nominatim;
-
-/**
- * A geographic point with a search radius.
- */
-class NearPoint
-{
-    private $fLat;
-    private $fLon;
-    private $fRadius;
-
-    private $sSQL;
-
-
-    public function __construct($lat, $lon, $radius = 0.1)
-    {
-        $this->fLat = (float)$lat;
-        $this->fLon = (float)$lon;
-        $this->fRadius = (float)$radius;
-        $this->sSQL = 'ST_SetSRID(ST_Point('.$this->fLon.','.$this->fLat.'),4326)';
-    }
-
-    public function lat()
-    {
-        return $this->fLat;
-    }
-
-    public function lon()
-    {
-        return $this->fLon;
-    }
-
-    public function radius()
-    {
-        return $this->fRadius;
-    }
-
-    public function distanceSQL($sObj)
-    {
-        return 'ST_Distance('.$this->sSQL.", $sObj)";
-    }
-
-    public function withinSQL($sObj)
-    {
-        return sprintf('ST_DWithin(%s, %s, %F)', $sObj, $this->sSQL, $this->fRadius);
-    }
-
-    /**
-     * Check that the coordinates are valid WSG84 coordinates.
-     *
-     * @return bool True if the coordinates are correctly bounded.
-     */
-    public function isValid()
-    {
-        return ($this->fLat <= 90.1
-                && $this->fLat >= -90.1
-                && $this->fLon <= 180.1
-                && $this->fLon >= -180.1);
-    }
-
-    /**
-     * Extract a coordinate point from a query string.
-     *
-     * If a coordinate is found an array of a new NearPoint and the
-     * remaining query is returned or false otherwise.
-     *
-     * @param string $sQuery Query to scan.
-     *
-     * @return array|false If a coordinate was found, an array with
-     *                     `pt` as the NearPoint coordinates and `query`
-     *                      with the remaining query string. False otherwiese.
-     */
-    public static function extractFromQuery($sQuery)
-    {
-        // Do we have anything that looks like a lat/lon pair?
-        // returns array(lat,lon,query_with_lat_lon_removed)
-        // or null
-        $sFound    = null;
-        $fQueryLat = null;
-        $fQueryLon = null;
-
-        if (preg_match('/\\s*([NS])[ ]+([0-9]+[0-9.]*)[° ]+([0-9.]+)?[′\']*[, ]+([EW])[ ]+([0-9]+)[° ]+([0-9]+[0-9.]*)[′\']*\\s*/', $sQuery, $aData)) {
-            /*               1         2                   3                    4         5            6
-             * degrees decimal minutes
-             * N 40 26.767, W 79 58.933
-             * N 40°26.767′, W 79°58.933′
-             */
-            $sFound    = $aData[0];
-            $fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60);
-            $fQueryLon = ($aData[4]=='E'?1:-1) * ($aData[5] + $aData[6]/60);
-        } elseif (preg_match('/\\s*([0-9]+)[° ]+([0-9]+[0-9.]*)?[′\']*[ ]+([NS])[, ]+([0-9]+)[° ]+([0-9]+[0-9.]*)?[′\' ]+([EW])\\s*/', $sQuery, $aData)) {
-            /*                     1            2                         3          4            5                      6
-             * degrees decimal minutes
-             * 40 26.767 N, 79 58.933 W
-             * 40° 26.767′ N 79° 58.933′ W
-             */
-            $sFound    = $aData[0];
-            $fQueryLat = ($aData[3]=='N'?1:-1) * ($aData[1] + $aData[2]/60);
-            $fQueryLon = ($aData[6]=='E'?1:-1) * ($aData[4] + $aData[5]/60);
-        } elseif (preg_match('/\\s*([NS])[ ]([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″"]*[, ]+([EW])[ ]([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″"]*\\s*/', $sQuery, $aData)) {
-            /*                     1        2            3              4                 5        6            7              8
-             * degrees decimal seconds
-             * N 40 26 46 W 79 58 56
-             * N 40° 26′ 46″, W 79° 58′ 56″
-             */
-            $sFound    = $aData[0];
-            $fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60 + $aData[4]/3600);
-            $fQueryLon = ($aData[5]=='E'?1:-1) * ($aData[6] + $aData[7]/60 + $aData[8]/3600);
-        } elseif (preg_match('/\\s*([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″" ]+([NS])[, ]+([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″" ]+([EW])\\s*/', $sQuery, $aData)) {
-            /*                     1            2              3             4          5            6              7             8
-             * degrees decimal seconds
-             * 40 26 46 N 79 58 56 W
-             * 40° 26′ 46″ N, 79° 58′ 56″ W
-             */
-            $sFound    = $aData[0];
-            $fQueryLat = ($aData[4]=='N'?1:-1) * ($aData[1] + $aData[2]/60 + $aData[3]/3600);
-            $fQueryLon = ($aData[8]=='E'?1:-1) * ($aData[5] + $aData[6]/60 + $aData[7]/3600);
-        } elseif (preg_match('/\\s*([NS])[ ]([0-9]+[0-9]*\\.[0-9]+)[°]*[, ]+([EW])[ ]([0-9]+[0-9]*\\.[0-9]+)[°]*\\s*/', $sQuery, $aData)) {
-            /*                     1        2                               3        4
-             * degrees decimal
-             * N 40.446° W 79.982°
-             */
-            $sFound    = $aData[0];
-            $fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2]);
-            $fQueryLon = ($aData[3]=='E'?1:-1) * ($aData[4]);
-        } elseif (preg_match('/\\s*([0-9]+[0-9]*\\.[0-9]+)[° ]+([NS])[, ]+([0-9]+[0-9]*\\.[0-9]+)[° ]+([EW])\\s*/', $sQuery, $aData)) {
-            /*                     1                           2          3                           4
-             * degrees decimal
-             * 40.446° N 79.982° W
-             */
-            $sFound    = $aData[0];
-            $fQueryLat = ($aData[2]=='N'?1:-1) * ($aData[1]);
-            $fQueryLon = ($aData[4]=='E'?1:-1) * ($aData[3]);
-        } elseif (preg_match('/(\\s*\\[|^\\s*|\\s*)(-?[0-9]+[0-9]*\\.[0-9]+)[, ]+(-?[0-9]+[0-9]*\\.[0-9]+)(\\]\\s*|\\s*$|\\s*)/', $sQuery, $aData)) {
-            /*                 1                   2                             3                        4
-             * degrees decimal
-             * 12.34, 56.78
-             * 12.34 56.78
-             * [12.456,-78.90]
-             */
-            $sFound    = $aData[0];
-            $fQueryLat = $aData[2];
-            $fQueryLon = $aData[3];
-        } else {
-            return false;
-        }
-
-        $oPt = new NearPoint($fQueryLat, $fQueryLon);
-
-        if (!$oPt->isValid()) return false;
-
-        $sQuery = trim(str_replace($sFound, ' ', $sQuery));
-
-        return array('pt' => $oPt, 'query' => $sQuery);
-    }
-}
index 1de0893c4a7b46fcc886b6d5d3bf9d7d9262558e..9b43a3e38cdd8d2aabd8c5391b0aeee90898631b 100644 (file)
@@ -66,15 +66,22 @@ class ReverseGeocode
         );
     }
 
+    public function lookup($fLat, $fLon, $bDoInterpolation = true)
+    {
+        return $this->lookupPoint(
+            'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)',
+            $bDoInterpolation
+        );
+    }
+
     /* lookup()
      * returns { place_id =>, type => '(osm|tiger)' }
      * fails if no place was found
      */
 
 
-    public function lookup($fLat, $fLon, $bDoInterpolation = true)
+    public function lookupPoint($sPointSQL, $bDoInterpolation = true)
     {
-        $sPointSQL = 'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)';
         $iMaxRank = $this->iMaxRank;
 
         // Find the nearest point
diff --git a/lib/SearchContext.php b/lib/SearchContext.php
new file mode 100644 (file)
index 0000000..a6b6358
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+namespace Nominatim;
+
+require_once(CONST_BasePath.'/lib/lib.php');
+
+
+/**
+ * Collects search constraints that are independent of the
+ * actual interpretation of the search query.
+ *
+ * The search context is shared between all SearchDescriptions. This
+ * object mainly serves as context provider for the database queries.
+ * Therefore most data is directly cached as SQL statements.
+ */
+class SearchContext
+{
+    private $fNearRadius = false;
+
+    // cached SQL
+
+    public $sqlNear = '';
+
+    public function hasNearPoint()
+    {
+        return $this->fNearRadius !== false;
+    }
+
+    public function nearRadius()
+    {
+        return $this->fNearRadius;
+    }
+
+    public function setNearPoint($fLat, $fLon, $fRadius = 0.1)
+    {
+        $this->fNearRadius = $fRadius;
+        $this->sqlNear = 'ST_SetSRID(ST_Point('.$fLon.','.$fLat.'),4326)';
+    }
+
+    /**
+     * Extract a coordinate point from a query string.
+     *
+     * @param string $sQuery Query to scan.
+     *
+     * @return The remaining query string.
+     */
+    public function setNearPointFromQuery($sQuery)
+    {
+        $aResult = parseLatLon($sQuery);
+
+        if ($aResult !== false
+            && $aResult[1] <= 90.1
+            && $aResult[1] >= -90.1
+            && $aResult[2] <= 180.1
+            && $aResult[2] >= -180.1
+        ) {
+            $this->setNearPoint($aResult[1], $aResult[2]);
+            $sQuery = trim(str_replace($aResult[0], ' ', $sQuery));
+        }
+
+        return $sQuery;
+    }
+
+    public function distanceSQL($sObj)
+    {
+        return 'ST_Distance('.$this->sqlNear.", $sObj)";
+    }
+
+    public function withinSQL($sObj)
+    {
+        return sprintf('ST_DWithin(%s, %s, %F)', $sObj, $this->sqlNear, $this->fNearRadius);
+    }
+}
index 42e5af309854b76967cc3079e35f32c0ceedae76..7073186b09a778f9c528fde56b90199d9f7084d0 100644 (file)
@@ -3,6 +3,7 @@
 namespace Nominatim;
 
 require_once(CONST_BasePath.'/lib/SpecialSearchOperator.php');
+require_once(CONST_BasePath.'/lib/SearchContext.php');
 
 /**
  * Description of a single interpretation of a search query.
@@ -33,15 +34,20 @@ class SearchDescription
     private $sHouseNumber = '';
     /// Postcode for the object.
     private $sPostcode = '';
-    /// Geographic search area.
-    private $oNearPoint = false;
+    /// Global search constraints.
+    private $oContext;
 
     // Temporary values used while creating the search description.
 
-    /// Index of phrase currently processed
+    /// Index of phrase currently processed.
     private $iNamePhrase = -1;
 
 
+    public function __construct($oContext)
+    {
+        $this->oContext = $oContext;
+    }
+
     public function getRank()
     {
         return $this->iSearchRank;
@@ -58,11 +64,6 @@ class SearchDescription
         return $this->sPostcode;
     }
 
-    public function setNear(&$oNearPoint)
-    {
-        $this->oNearPoint = $oNearPoint;
-    }
-
     public function setPoiSearch($iOperator, $sClass, $sType)
     {
         $this->iOperator = $iOperator;
@@ -78,12 +79,7 @@ class SearchDescription
     public function isCountrySearch()
     {
         return $this->sCountryCode && sizeof($this->aName) == 0
-               && !$this->iOperator && !$this->oNearPoint;
-    }
-
-    public function isNearSearch()
-    {
-        return (bool) $this->oNearPoint;
+               && !$this->iOperator && !$this->oContext->hasNearPoint();
     }
 
     public function isPoiSearch()
@@ -400,8 +396,8 @@ class SearchDescription
             if ($sCountryList) {
                 $sSQL .= ' JOIN placex USING (place_id)';
             }
-            if ($this->oNearPoint) {
-                $sSQL .= ' WHERE '.$this->oNearPoint->withinSQL('ct.centroid');
+            if ($this->oContext->hasNearPoint()) {
+                $sSQL .= ' WHERE '.$this->oContext->withinSQL('ct.centroid');
             } else {
                 $sSQL .= " WHERE ST_Contains($sViewboxSQL, ct.centroid)";
             }
@@ -413,23 +409,23 @@ class SearchDescription
             }
             if ($sViewboxCentreSQL) {
                 $sSQL .= " ORDER BY ST_Distance($sViewboxCentreSQL, ct.centroid) ASC";
-            } elseif ($this->oNearPoint) {
-                $sSQL .= ' ORDER BY '.$this->oNearPoint->distanceSQL('ct.centroid').' ASC';
+            } elseif ($this->oContext->hasNearPoint()) {
+                $sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('ct.centroid').' ASC';
             }
             $sSQL .= " limit $iLimit";
             if (CONST_Debug) var_dump($sSQL);
             return chksql($oDB->getCol($sSQL));
         }
 
-        if ($this->oNearPoint) {
+        if ($this->oContext->hasNearPoint()) {
             $sSQL = 'SELECT place_id FROM placex WHERE ';
             $sSQL .= 'class=\''.$this->sClass."' and type='".$this->sType."'";
-            $sSQL .= ' AND '.$this->oNearPoint->withinSQL('geometry');
+            $sSQL .= ' AND '.$this->oContext->withinSQL('geometry');
             $sSQL .= ' AND linked_place_id is null';
             if ($sCountryList) {
                 $sSQL .= " AND country_code in ($sCountryList)";
             }
-            $sSQL .= ' ORDER BY '.$this->oNearPoint->distanceSQL('centroid')." ASC";
+            $sSQL .= ' ORDER BY '.$this->oContext->distanceSQL('centroid')." ASC";
             $sSQL .= " LIMIT $iLimit";
             if (CONST_Debug) var_dump($sSQL);
             return chksql($oDB->getCol($sSQL));
@@ -526,9 +522,9 @@ class SearchDescription
             }
         }
 
-        if ($this->oNearPoint) {
-            $aTerms[] = $this->oNearPoint->withinSQL('centroid');
-            $aOrder[] = $this->oNearPoint->distanceSQL('centroid');
+        if ($this->oContext->hasNearPoint()) {
+            $aTerms[] = $this->oContext->withinSQL('centroid');
+            $aOrder[] = $this->oContext->distanceSQL('centroid');
         } elseif ($this->sPostcode) {
             if (!sizeof($this->aAddress)) {
                 $aTerms[] = "EXISTS(SELECT place_id FROM location_postcode p WHERE p.postcode = '".$this->sPostcode."' AND ST_DWithin(search_name.centroid, p.geometry, 0.1))";
@@ -545,8 +541,8 @@ class SearchDescription
             $aTerms[] = 'centroid && '.$sViewboxSmall;
         }
 
-        if ($this->oNearPoint) {
-            $aOrder[] = $this->oNearPoint->distanceSQL('centroid');
+        if ($this->oContext->hasNearPoint()) {
+            $aOrder[] = $this->oContext->distanceSQL('centroid');
         }
 
         if ($this->sHouseNumber) {
@@ -765,8 +761,8 @@ class SearchDescription
                     $fRange = 0.05;
 
                     $sOrderBySQL = '';
-                    if ($this->oNearPoint) {
-                        $sOrderBySQL = $this->oNearPoint->distanceSQL('l.centroid');
+                    if ($this->oContext->hasNearPoint()) {
+                        $sOrderBySQL = $this->oContext->distanceSQL('l.centroid');
                     } elseif ($sPlaceIDs) {
                         $sOrderBySQL = "ST_Distance(l.centroid, f.geometry)";
                     } elseif ($sPlaceGeom) {
@@ -804,13 +800,13 @@ class SearchDescription
 
                     $aClassPlaceIDs = array_merge($aClassPlaceIDs, chksql($oDB->getCol($sSQL)));
                 } else {
-                    if ($this->oNearPoint) {
-                        $fRange = $this->oNearPoint->radius();
+                    if ($this->oContext->hasNearPoint()) {
+                        $fRange = $this->oContext->nearRadius();
                     }
 
                     $sOrderBySQL = '';
-                    if ($this->oNearPoint) {
-                        $sOrderBySQL = $this->oNearPoint->distanceSQL('l.geometry');
+                    if ($this->oContext->hasNearPoint()) {
+                        $sOrderBySQL = $this->oContext->distanceSQL('l.geometry');
                     } else {
                         $sOrderBySQL = "ST_Distance(l.geometry, f.geometry)";
                     }
@@ -878,14 +874,6 @@ class SearchDescription
         echo "<td>".$this->sPostcode."</td>";
         echo "<td>".$this->sHouseNumber."</td>";
 
-        if ($this->oNearPoint) {
-            echo "<td>".$this->oNearPoint->lat()."</td>";
-            echo "<td>".$this->oNearPoint->lon()."</td>";
-            echo "<td>".$this->oNearPoint->radius()."</td>";
-        } else {
-            echo "<td></td><td></td><td></td>";
-        }
-
         echo "</tr>";
     }
 }
index 969d58a33cc353d8f9f3fe7506b81e0cbc6afa73..b5fbee3e600b08548119d7e076a40fd3a77dd4ed 100644 (file)
@@ -489,9 +489,8 @@ function _debugDumpGroupedSearches($aData, $aTokens)
     }
     echo "<table border=\"1\">";
     echo "<tr><th>rank</th><th>Name Tokens</th><th>Name Not</th>";
-    echo "<th>Address Tokens</th><th>Address Not</th><th>country</th>";
-    echo "<th>operator</th><th>class</th><th>type</th><th>postcode</th><th>house#</th>";
-    echo "<th>Lat</th><th>Lon</th><th>Radius</th></tr>";
+    echo "<th>Address Tokens</th><th>Address Not</th><th>country</th><th>operator</th>";
+    echo "<th>class</th><th>type</th><th>postcode</th><th>housenumber</th></tr>";
     foreach ($aData as $iRank => $aRankedSet) {
         foreach ($aRankedSet as $aRow) {
             $aRow->dumpAsHtmlTableRow($aWordsIDs);
@@ -546,6 +545,81 @@ function addQuotes($s)
     return "'".$s."'";
 }
 
+function parseLatLon($sQuery)
+{
+    $sFound    = null;
+    $fQueryLat = null;
+    $fQueryLon = null;
+
+    if (preg_match('/\\s*([NS])[ ]+([0-9]+[0-9.]*)[° ]+([0-9.]+)?[′\']*[, ]+([EW])[ ]+([0-9]+)[° ]+([0-9]+[0-9.]*)[′\']*\\s*/', $sQuery, $aData)) {
+        /*               1         2                   3                    4         5            6
+         * degrees decimal minutes
+         * N 40 26.767, W 79 58.933
+         * N 40°26.767′, W 79°58.933′
+         */
+        $sFound    = $aData[0];
+        $fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60);
+        $fQueryLon = ($aData[4]=='E'?1:-1) * ($aData[5] + $aData[6]/60);
+    } elseif (preg_match('/\\s*([0-9]+)[° ]+([0-9]+[0-9.]*)?[′\']*[ ]+([NS])[, ]+([0-9]+)[° ]+([0-9]+[0-9.]*)?[′\' ]+([EW])\\s*/', $sQuery, $aData)) {
+        /*                     1            2                         3          4            5                      6
+         * degrees decimal minutes
+         * 40 26.767 N, 79 58.933 W
+         * 40° 26.767′ N 79° 58.933′ W
+         */
+        $sFound    = $aData[0];
+        $fQueryLat = ($aData[3]=='N'?1:-1) * ($aData[1] + $aData[2]/60);
+        $fQueryLon = ($aData[6]=='E'?1:-1) * ($aData[4] + $aData[5]/60);
+    } elseif (preg_match('/\\s*([NS])[ ]([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″"]*[, ]+([EW])[ ]([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″"]*\\s*/', $sQuery, $aData)) {
+        /*                     1        2            3              4                 5        6            7              8
+         * degrees decimal seconds
+         * N 40 26 46 W 79 58 56
+         * N 40° 26′ 46″, W 79° 58′ 56″
+         */
+        $sFound    = $aData[0];
+        $fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2] + $aData[3]/60 + $aData[4]/3600);
+        $fQueryLon = ($aData[5]=='E'?1:-1) * ($aData[6] + $aData[7]/60 + $aData[8]/3600);
+    } elseif (preg_match('/\\s*([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″" ]+([NS])[, ]+([0-9]+)[° ]+([0-9]+)[′\' ]+([0-9]+)[″" ]+([EW])\\s*/', $sQuery, $aData)) {
+        /*                     1            2              3             4          5            6              7             8
+         * degrees decimal seconds
+         * 40 26 46 N 79 58 56 W
+         * 40° 26′ 46″ N, 79° 58′ 56″ W
+         */
+        $sFound    = $aData[0];
+        $fQueryLat = ($aData[4]=='N'?1:-1) * ($aData[1] + $aData[2]/60 + $aData[3]/3600);
+        $fQueryLon = ($aData[8]=='E'?1:-1) * ($aData[5] + $aData[6]/60 + $aData[7]/3600);
+    } elseif (preg_match('/\\s*([NS])[ ]([0-9]+[0-9]*\\.[0-9]+)[°]*[, ]+([EW])[ ]([0-9]+[0-9]*\\.[0-9]+)[°]*\\s*/', $sQuery, $aData)) {
+        /*                     1        2                               3        4
+         * degrees decimal
+         * N 40.446° W 79.982°
+         */
+        $sFound    = $aData[0];
+        $fQueryLat = ($aData[1]=='N'?1:-1) * ($aData[2]);
+        $fQueryLon = ($aData[3]=='E'?1:-1) * ($aData[4]);
+    } elseif (preg_match('/\\s*([0-9]+[0-9]*\\.[0-9]+)[° ]+([NS])[, ]+([0-9]+[0-9]*\\.[0-9]+)[° ]+([EW])\\s*/', $sQuery, $aData)) {
+        /*                     1                           2          3                           4
+         * degrees decimal
+         * 40.446° N 79.982° W
+         */
+        $sFound    = $aData[0];
+        $fQueryLat = ($aData[2]=='N'?1:-1) * ($aData[1]);
+        $fQueryLon = ($aData[4]=='E'?1:-1) * ($aData[3]);
+    } elseif (preg_match('/(\\s*\\[|^\\s*|\\s*)(-?[0-9]+[0-9]*\\.[0-9]+)[, ]+(-?[0-9]+[0-9]*\\.[0-9]+)(\\]\\s*|\\s*$|\\s*)/', $sQuery, $aData)) {
+        /*                 1                   2                             3                        4
+         * degrees decimal
+         * 12.34, 56.78
+         * 12.34 56.78
+         * [12.456,-78.90]
+         */
+        $sFound    = $aData[0];
+        $fQueryLat = $aData[2];
+        $fQueryLon = $aData[3];
+    } else {
+        return false;
+    }
+
+    return array($sFound, $fQueryLat, $fQueryLon);
+}
+
 
 function geometryText2Points($geometry_as_text, $fRadius)
 {