Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 155 |
QueryCoordinates | |
0.00% |
0 / 1 |
|
0.00% |
0 / 7 |
870 | |
0.00% |
0 / 155 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
execute | |
0.00% |
0 / 1 |
182 | |
0.00% |
0 / 63 |
|||
getFromCoord | |
0.00% |
0 / 1 |
132 | |
0.00% |
0 / 46 |
|||
getCacheMode | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
getAllowedParams | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 34 |
|||
getExamplesMessages | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 5 |
|||
getHelpUrls | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
<?php | |
namespace GeoData\Api; | |
use ApiBase; | |
use ApiQuery; | |
use ApiQueryBase; | |
use GeoData\Coord; | |
use GeoData\GeoData; | |
use GeoData\Globe; | |
use GeoData\Math; | |
use MediaWiki\Page\WikiPageFactory; | |
use MWException; | |
use Title; | |
use Wikimedia\ParamValidator\ParamValidator; | |
use Wikimedia\ParamValidator\TypeDef\IntegerDef; | |
/** | |
* This query adds an <coordinates> subelement to all pages with the list of coordinated | |
* present on those pages. | |
*/ | |
class QueryCoordinates extends ApiQueryBase { | |
/** @var WikiPageFactory */ | |
private $wikiPageFactory; | |
/** | |
* @param ApiQuery $query | |
* @param string $moduleName | |
* @param WikiPageFactory $wikiPageFactory | |
*/ | |
public function __construct( ApiQuery $query, $moduleName, WikiPageFactory $wikiPageFactory ) { | |
parent::__construct( $query, $moduleName, 'co' ); | |
$this->wikiPageFactory = $wikiPageFactory; | |
} | |
public function execute(): void { | |
$titles = $this->getPageSet()->getGoodTitles(); | |
if ( $titles === [] ) { | |
return; | |
} | |
$params = $this->extractRequestParams(); | |
$from = $this->getFromCoord( $params ); | |
$this->addTables( 'geo_tags' ); | |
$this->addFields( [ 'gt_id', 'gt_page_id', 'gt_lat', 'gt_lon', 'gt_primary', 'gt_globe' ] ); | |
foreach ( $params['prop'] as $prop ) { | |
if ( isset( Coord::FIELD_MAPPING[$prop] ) ) { | |
$this->addFields( Coord::FIELD_MAPPING[$prop] ); | |
} | |
} | |
$this->addWhereFld( 'gt_page_id', array_keys( $titles ) ); | |
$primary = $params['primary']; | |
$this->addWhereIf( | |
[ 'gt_primary' => intval( $primary === 'primary' ) ], $primary !== 'all' | |
); | |
if ( isset( $params['continue'] ) ) { | |
$parts = explode( '|', $params['continue'], 3 ); | |
$this->dieContinueUsageIf( count( $parts ) !== 2 ); | |
$this->dieContinueUsageIf( !is_numeric( $parts[0] ) ); | |
$this->dieContinueUsageIf( !is_numeric( $parts[1] ) ); | |
$parts[0] = intval( $parts[0] ); | |
$parts[1] = intval( $parts[1] ); | |
$this->addWhere( | |
"gt_page_id > {$parts[0]} OR ( gt_page_id = {$parts[0]} AND gt_id >= {$parts[1]} )" | |
); | |
} else { | |
$this->addOption( 'USE INDEX', 'gt_page_id' ); | |
} | |
$this->addOption( 'ORDER BY', [ 'gt_page_id', 'gt_id' ] ); | |
$this->addOption( 'LIMIT', $params['limit'] + 1 ); | |
$res = $this->select( __METHOD__ ); | |
$count = 0; | |
foreach ( $res as $row ) { | |
if ( ++$count > $params['limit'] ) { | |
$this->setContinueEnumParameter( 'continue', $row->gt_page_id . '|' . $row->gt_id ); | |
break; | |
} | |
$vals = [ | |
'lat' => floatval( $row->gt_lat ), | |
'lon' => floatval( $row->gt_lon ), | |
'primary' => boolval( $row->gt_primary ), | |
]; | |
foreach ( $params['prop'] as $prop ) { | |
$column = Coord::FIELD_MAPPING[$prop] ?? null; | |
if ( $column && isset( $row->$column ) ) { | |
$vals[$prop] = $row->$column; | |
} | |
} | |
if ( $from && $row->gt_globe == $from->globe ) { | |
$vals['dist'] = round( | |
Math::distance( $from->lat, $from->lon, $row->gt_lat, $row->gt_lon ), | |
1 | |
); | |
} | |
$fit = $this->addPageSubItem( $row->gt_page_id, $vals ); | |
if ( !$fit ) { | |
$this->setContinueEnumParameter( 'continue', $row->gt_page_id . '|' . $row->gt_id ); | |
} | |
} | |
} | |
/** | |
* @param array $params | |
* @return Coord|null | |
* @throws MWException | |
*/ | |
private function getFromCoord( array $params ): ?Coord { | |
$this->requireMaxOneParameter( $params, 'distancefrompoint', 'distancefrompage' ); | |
$globe = new Globe( 'earth' ); | |
if ( $params['distancefrompoint'] !== null ) { | |
$arr = explode( '|', $params['distancefrompoint'] ); | |
if ( count( $arr ) != 2 || !$globe->coordinatesAreValid( $arr[0], $arr[1] ) ) { | |
$this->dieWithError( 'apierror-geodata-badcoord', 'invalid-coord' ); | |
} | |
return new Coord( (float)$arr[0], (float)$arr[1], 'earth' ); | |
} | |
if ( $params['distancefrompage'] !== null ) { | |
$title = Title::newFromText( $params['distancefrompage'] ); | |
if ( !$title ) { | |
$this->dieWithError( [ | |
'apierror-invalidtitle', | |
wfEscapeWikiText( $params['distancefrompage'] ) | |
] ); | |
} | |
// @phan-suppress-next-line PhanTypeMismatchArgumentNullable T240141 | |
$page = $this->wikiPageFactory->newFromTitle( $title ); | |
if ( !$page->exists() ) { | |
$this->dieWithError( [ | |
'apierror-invalidtitle', | |
wfEscapeWikiText( $params['distancefrompage'] ) | |
] ); | |
} | |
$redirectTarget = $page->getRedirectTarget(); | |
if ( $redirectTarget ) { | |
$title = $redirectTarget; | |
$page = $this->wikiPageFactory->newFromTitle( $title ); | |
} | |
// @phan-suppress-next-line PhanTypeMismatchArgumentNullable T240141 | |
$coord = GeoData::getPageCoordinates( $title ); | |
if ( !$coord ) { | |
$this->dieWithError( | |
[ | |
'apierror-geodata-noprimarycoord', | |
wfEscapeWikiText( $title->getPrefixedText() ) | |
], | |
'no-coordinates' | |
); | |
} | |
if ( $coord->globe != 'earth' ) { | |
$this->dieWithError( 'apierror-geodata-notonearth', 'notonearth' ); | |
} | |
return $coord; | |
} | |
return null; | |
} | |
/** @inheritDoc */ | |
public function getCacheMode( $params ) { | |
return 'public'; | |
} | |
/** @inheritDoc */ | |
public function getAllowedParams() { | |
return [ | |
'limit' => [ | |
ParamValidator::PARAM_DEFAULT => 10, | |
ParamValidator::PARAM_TYPE => 'limit', | |
IntegerDef::PARAM_MIN => 1, | |
IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1, | |
IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2 | |
], | |
'continue' => [ | |
ParamValidator::PARAM_TYPE => 'string', | |
ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', | |
], | |
'prop' => [ | |
ParamValidator::PARAM_TYPE => [ 'type', 'name', 'dim', 'country', 'region', 'globe' ], | |
ParamValidator::PARAM_DEFAULT => 'globe', | |
ParamValidator::PARAM_ISMULTI => true, | |
ApiBase::PARAM_HELP_MSG_PER_VALUE => [], | |
], | |
'primary' => [ | |
ParamValidator::PARAM_TYPE => [ 'primary', 'secondary', 'all' ], | |
ParamValidator::PARAM_DEFAULT => 'primary', | |
ApiBase::PARAM_HELP_MSG_PER_VALUE => [], | |
], | |
'distancefrompoint' => [ | |
ParamValidator::PARAM_TYPE => 'string', | |
ApiBase::PARAM_HELP_MSG_APPEND => [ | |
'geodata-api-help-coordinates-format', | |
], | |
], | |
'distancefrompage' => [ | |
ParamValidator::PARAM_TYPE => 'string', | |
], | |
]; | |
} | |
/** @inheritDoc */ | |
protected function getExamplesMessages() { | |
return [ | |
'action=query&prop=coordinates&titles=Main%20Page' | |
=> 'apihelp-query+coordinates-example-1', | |
]; | |
} | |
/** @inheritDoc */ | |
public function getHelpUrls() { | |
return 'https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:GeoData#prop.3Dcoordinates'; | |
} | |
} |