/** * @dataProvider getRectData */ public function testRectWrapAround($lon) { $r = GeoMath::rectAround(20, $lon, 10000); $this->assertGreaterThan($r['maxLon'], $r['minLon']); $this->assertGreaterThanOrEqual(-180, $r['minLon']); $this->assertLessThanOrEqual(180, $r['maxLon']); }
public function testRectAround() { for ($i = 0; $i < 90; $i += 5) { $r = GeoMath::rectAround($i, $i, 5000); $this->assertEquals(10000, GeoMath::distance($i, $r['minLon'], $i, $r['maxLon']), 'rectAround(): test longitude', 1); $this->assertEquals(10000, GeoMath::distance($r['minLat'], $i, $r['maxLat'], $i), 'rectAround(): test latitude', 1); } }
/** * @param ApiPageSet $resultPageSet * @return */ private function run($resultPageSet = null) { $params = $this->extractRequestParams(); $exclude = false; $this->requireOnlyOneParameter($params, 'coord', 'page'); if (isset($params['coord'])) { $arr = explode('|', $params['coord']); if (count($arr) != 2 || !GeoData::validateCoord($arr[0], $arr[1], $params['globe'])) { $this->dieUsage('Invalid coordinate provided', '_invalid-coord'); } $lat = $arr[0]; $lon = $arr[1]; } elseif (isset($params['page'])) { $t = Title::newFromText($params['page']); if (!$t || !$t->canExist()) { $this->dieUsage("Invalid page title ``{$params['page']}'' provided", '_invalid-page'); } if (!$t->exists()) { $this->dieUsage("Page ``{$params['page']}'' does not exist", '_nonexistent-page'); } $coord = GeoData::getPageCoordinates($t); if (!$coord) { $this->dieUsage('Page coordinates unknown', '_no-coordinates'); } $lat = $coord->lat; $lon = $coord->lon; $exclude = $t->getArticleID(); } $lat = floatval($lat); $lon = floatval($lon); $radius = intval($params['radius']); $rect = GeoMath::rectAround($lat, $lon, $radius); $dbr = wfGetDB(DB_SLAVE); $this->addTables(array('page', 'geo_tags')); $this->addFields(array('gt_lat', 'gt_lon', 'gt_primary')); // retrieve some fields only if page set needs them if (is_null($resultPageSet)) { $this->addFields(array('page_id', 'page_namespace', 'page_title')); } else { $this->addFields(WikiPage::selectFields()); } foreach ($params['prop'] as $prop) { if (isset(Coord::$fieldMapping[$prop])) { $this->addFields(Coord::$fieldMapping[$prop]); } } $this->addWhereFld('gt_globe', $params['globe']); $this->addWhereFld('gt_lat_int', self::intRange($rect["minLat"], $rect["maxLat"])); $this->addWhereFld('gt_lon_int', self::intRange($rect["minLon"], $rect["maxLon"])); $this->addWhereRange('gt_lat', 'newer', $rect["minLat"], $rect["maxLat"], false); if ($rect["minLon"] > $rect["maxLon"]) { $this->addWhere("gt_lon < {$rect['maxLon']} OR gt_lon > {$rect['minLon']}"); } else { $this->addWhereRange('gt_lon', 'newer', $rect["minLon"], $rect["maxLon"], false); } $this->addWhereFld('page_namespace', $params['namespace']); $this->addWhere('gt_page_id = page_id'); if ($exclude) { $this->addWhere("gt_page_id <> {$exclude}"); } if (isset($params['maxdim'])) { $this->addWhere('gt_dim < ' . intval($params['maxdim'])); } $primary = array_flip($params['primary']); $this->addWhereIf(array('gt_primary' => 1), isset($primary['yes']) && !isset($primary['no'])); $this->addWhereIf(array('gt_primary' => 0), !isset($primary['yes']) && isset($primary['no'])); $this->addOption('USE INDEX', 'gt_spatial'); $limit = $params['limit']; $res = $this->select(__METHOD__); $rows = array(); foreach ($res as $row) { $row->dist = GeoMath::distance($lat, $lon, $row->gt_lat, $row->gt_lon); $rows[] = $row; } // sort in PHP because sorting via SQL involves a filesort usort($rows, 'ApiQueryGeoSearch::compareRows'); $result = $this->getResult(); foreach ($rows as $row) { if (!$limit--) { break; } if (is_null($resultPageSet)) { $title = Title::newFromRow($row); $vals = array('pageid' => intval($row->page_id), 'ns' => intval($title->getNamespace()), 'title' => $title->getPrefixedText(), 'lat' => floatval($row->gt_lat), 'lon' => floatval($row->gt_lon), 'dist' => round($row->dist, 1)); if ($row->gt_primary) { $vals['primary'] = ''; } foreach ($params['prop'] as $prop) { if (isset(Coord::$fieldMapping[$prop]) && isset($row->{Coord::$fieldMapping[$prop]})) { $field = Coord::$fieldMapping[$prop]; $vals[$prop] = $row->{$field}; } } $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { break; } } else { $resultPageSet->processDbRow($row); } } if (is_null($resultPageSet)) { $result->setIndexedTagName_internal(array('query', $this->getModuleName()), $this->getModulePrefix()); } }
private static function parseOneCoord($parts, $coordInfo) { global $wgContLang; $count = count($parts); $multiplier = 1; $value = 0; for ($i = 0; $i < $count; $i++) { $part = $parts[$i]; if ($i > 0 && $i == $count - 1) { $suffix = self::parseSuffix($part, $coordInfo); if ($suffix) { if ($value < 0) { return false; // "-60°S sounds weird, isn't it? } $value *= $suffix; break; } elseif ($i == 3) { return false; } } $part = $wgContLang->parseFormattedNumber($part); $min = $i == 0 ? $coordInfo['min'] : 0; $max = $i == 0 ? $coordInfo['max'] : 59.999999; if (!is_numeric($part) || $part < $min || $part > $max) { return false; } $value += $part * $multiplier * GeoMath::sign($value); $multiplier /= 60; } if ($coordInfo['wrap'] && $value < 0) { $value = $coordInfo['max'] + $value; } if ($value < $coordInfo['min'] || $value > $coordInfo['max']) { return false; } return $value; }
/** * @param $resultPageSet ApiPageSet * @return */ private function run($resultPageSet = null) { $params = $this->extractRequestParams(); $exclude = false; $this->requireOnlyOneParameter($params, 'coord', 'page'); if (isset($params['coord'])) { $arr = explode('|', $params['coord']); if (count($arr) != 2 || !GeoData::validateCoord($arr[0], $arr[1], $params['globe'])) { $this->dieUsage('Invalid coordinate provided', '_invalid-coord'); } $lat = $arr[0]; $lon = $arr[1]; } elseif (isset($params['page'])) { $t = Title::newFromText($params['page']); if (!$t || !$t->canExist()) { $this->dieUsage("Invalid page title ``{$params['page']}'' provided", '_invalid-page'); } if (!$t->exists()) { $this->dieUsage("Page ``{$params['page']}'' does not exist", '_nonexistent-page'); } $coord = GeoData::getPageCoordinates($t); if (!$coord) { $this->dieUsage('Page coordinates unknown', '_no-coordinates'); } $lat = $coord->lat; $lon = $coord->lon; $exclude = $t->getArticleID(); } $lat = floatval($lat); $lon = floatval($lon); $radius = intval($params['radius']); $rect = GeoMath::rectAround($lat, $lon, $radius); $dbr = wfGetDB(DB_SLAVE); $this->addTables(array('geo_tags', 'page')); $this->addFields(array('gt_lat', 'gt_lon', 'gt_primary', "{$dbr->tablePrefix()}gd_distance( {$lat}, {$lon}, gt_lat, gt_lon ) AS dist")); // retrieve some fields only if page set needs them if (is_null($resultPageSet)) { $this->addFields(array('page_id', 'page_namespace', 'page_title')); } else { $this->addFields(array("{$dbr->tableName('page')}.*")); } foreach ($params['prop'] as $prop) { if (isset(Coord::$fieldMapping[$prop])) { $this->addFields(Coord::$fieldMapping[$prop]); } } $this->addWhereFld('gt_globe', $params['globe']); $this->addWhereRange('gt_lat', 'newer', $rect["minLat"], $rect["maxLat"], false); $this->addWhereRange('gt_lon', 'newer', $rect["minLon"], $rect["maxLon"], false); //$this->addWhere( 'dist < ' . intval( $radius ) ); hasta be in HAVING, not WHERE $this->addWhereFld('page_namespace', $params['namespace']); $this->addWhere('gt_page_id = page_id'); if ($exclude) { $this->addWhere("gt_page_id <> {$exclude}"); } if (isset($params['maxdim'])) { $this->addWhere('gt_dim < ' . intval($params['maxdim'])); } $primary = array_flip($params['primary']); $this->addWhereIf(array('gt_primary' => 1), isset($primary['yes']) && !isset($primary['no'])); $this->addWhereIf(array('gt_primary' => 0), !isset($primary['yes']) && isset($primary['no'])); $this->addOption('ORDER BY', 'dist'); $limit = $params['limit']; $this->addOption('LIMIT', $limit); $res = $this->select(__METHOD__); $result = $this->getResult(); foreach ($res as $row) { if (is_null($resultPageSet)) { $title = Title::newFromRow($row); $vals = array('pageid' => intval($row->page_id), 'ns' => intval($title->getNamespace()), 'title' => $title->getPrefixedText(), 'lat' => floatval($row->gt_lat), 'lon' => floatval($row->gt_lon), 'dist' => round($row->dist, 1)); if ($row->gt_primary) { $vals['primary'] = ''; } foreach ($params['prop'] as $prop) { if (isset(Coord::$fieldMapping[$prop]) && isset($row->{$field})) { $field = Coord::$fieldMapping[$prop]; $vals[$prop] = $row->{$field}; } } $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { break; } } else { $resultPageSet->processDbRow($row); } } }
private function order($dest_address) { $geo = GeoLookup::getLatLon($dest_address); $orders_by_name = $orders_by_id = []; echo "You want to create an order shipping to " . $geo[2]; ?> Enter one product name per line, empty line when order is done Optionally, enter <product name>=<quantity> to add multiple ><?php // allow user to enter the items for this order $stdin = fopen('php://stdin', 'r'); while ($line = trim(fgets($stdin))) { // handle optional <product name>=<quantity> $tmp = strrpos($line, '='); if ($tmp !== false && ctype_digit(substr($line, $tmp + 1))) { $quantity = substr($line, $tmp + 1); $line = substr($line, 0, $tmp); } else { $quantity = 1; } // attempt to find product, add it to the order try { $product = Db\ProductApi::getProduct($this->mysqli, $line); echo "Product added to order (quantity=" . $quantity . ")\n>"; } catch (\Exception $e) { echo "Cannot find the product -- please try again\n>"; continue; } @($orders_by_name[$product->name] += $quantity); @($orders_by_id[$product->id] += $quantity); } fclose($stdin); echo "Order Summary:\n"; foreach ($orders_by_name as $product => $quantity) { echo ' ' . substr($product . str_repeat('.', 30), 0, 30) . ' ' . $quantity . "\n"; } // get all warehouses with the required inventory, then pick the closest one $warehouses = Db\WarehouseApi::getStockedWarehouses($this->mysqli, $orders_by_id); $closest = null; $best_dist = null; foreach ($warehouses as $i) { $this_dist = GeoMath::vincentyGreatCircleDistance($geo[0], $geo[1], $i->lat, $i->lon); if ($best_dist === null || $this_dist < $best_dist) { $closest = $i; $best_dist = $this_dist; } } // final results if ($closest === null) { echo "\nNo single warehouse has all of the items requested. Sorry\n"; } else { echo "\nThis order will be fulfilled by this warehouse: " . $closest->name . "\n"; echo "The order destination is " . number_format($best_dist, 1) . "km away from the warehouse.\n"; } }