Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 119 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
QueryCoordinates | |
0.00% |
0 / 119 |
|
0.00% |
0 / 7 |
812 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 47 |
|
0.00% |
0 / 1 |
182 | |||
getFromCoord | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
110 | |||
getCacheMode | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getAllowedParams | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
2 | |||
getExamplesMessages | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
getHelpUrls | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace GeoData\Api; |
4 | |
5 | use ApiBase; |
6 | use ApiQuery; |
7 | use ApiQueryBase; |
8 | use GeoData\Coord; |
9 | use GeoData\GeoData; |
10 | use GeoData\Globe; |
11 | use GeoData\Math; |
12 | use MediaWiki\Page\WikiPageFactory; |
13 | use MediaWiki\Title\Title; |
14 | use Wikimedia\ParamValidator\ParamValidator; |
15 | use Wikimedia\ParamValidator\TypeDef\IntegerDef; |
16 | |
17 | /** |
18 | * This query adds an <coordinates> subelement to all pages with the list of coordinated |
19 | * present on those pages. |
20 | */ |
21 | class QueryCoordinates extends ApiQueryBase { |
22 | |
23 | /** @var WikiPageFactory */ |
24 | private $wikiPageFactory; |
25 | |
26 | /** |
27 | * @param ApiQuery $query |
28 | * @param string $moduleName |
29 | * @param WikiPageFactory $wikiPageFactory |
30 | */ |
31 | public function __construct( ApiQuery $query, $moduleName, WikiPageFactory $wikiPageFactory ) { |
32 | parent::__construct( $query, $moduleName, 'co' ); |
33 | $this->wikiPageFactory = $wikiPageFactory; |
34 | } |
35 | |
36 | public function execute(): void { |
37 | $titles = $this->getPageSet()->getGoodTitles(); |
38 | if ( $titles === [] ) { |
39 | return; |
40 | } |
41 | |
42 | $params = $this->extractRequestParams(); |
43 | $from = $this->getFromCoord( $params ); |
44 | $this->addTables( 'geo_tags' ); |
45 | $this->addFields( [ 'gt_id', 'gt_page_id', 'gt_lat', 'gt_lon', 'gt_primary', 'gt_globe' ] ); |
46 | foreach ( $params['prop'] as $prop ) { |
47 | if ( isset( Coord::FIELD_MAPPING[$prop] ) ) { |
48 | $this->addFields( Coord::FIELD_MAPPING[$prop] ); |
49 | } |
50 | } |
51 | $this->addWhereFld( 'gt_page_id', array_keys( $titles ) ); |
52 | $primary = $params['primary']; |
53 | $this->addWhereIf( |
54 | [ 'gt_primary' => intval( $primary === 'primary' ) ], $primary !== 'all' |
55 | ); |
56 | |
57 | if ( isset( $params['continue'] ) ) { |
58 | $parts = $this->parseContinueParamOrDie( $params['continue'], [ 'int', 'int' ] ); |
59 | $this->addWhere( $this->getDB()->buildComparison( '>=', [ |
60 | 'gt_page_id' => $parts[0], |
61 | 'gt_id' => $parts[1], |
62 | ] ) ); |
63 | } else { |
64 | $this->addOption( 'USE INDEX', 'gt_page_id' ); |
65 | } |
66 | |
67 | $this->addOption( 'ORDER BY', [ 'gt_page_id', 'gt_id' ] ); |
68 | $this->addOption( 'LIMIT', $params['limit'] + 1 ); |
69 | |
70 | $res = $this->select( __METHOD__ ); |
71 | |
72 | $count = 0; |
73 | foreach ( $res as $row ) { |
74 | if ( ++$count > $params['limit'] ) { |
75 | $this->setContinueEnumParameter( 'continue', $row->gt_page_id . '|' . $row->gt_id ); |
76 | break; |
77 | } |
78 | $vals = [ |
79 | 'lat' => floatval( $row->gt_lat ), |
80 | 'lon' => floatval( $row->gt_lon ), |
81 | 'primary' => boolval( $row->gt_primary ), |
82 | ]; |
83 | foreach ( $params['prop'] as $prop ) { |
84 | $column = Coord::FIELD_MAPPING[$prop] ?? null; |
85 | if ( $column && isset( $row->$column ) ) { |
86 | $vals[$prop] = $row->$column; |
87 | } |
88 | } |
89 | if ( $from && $row->gt_globe == $from->globe ) { |
90 | $vals['dist'] = round( |
91 | Math::distance( $from->lat, $from->lon, $row->gt_lat, $row->gt_lon ), |
92 | 1 |
93 | ); |
94 | } |
95 | $fit = $this->addPageSubItem( $row->gt_page_id, $vals ); |
96 | if ( !$fit ) { |
97 | $this->setContinueEnumParameter( 'continue', $row->gt_page_id . '|' . $row->gt_id ); |
98 | } |
99 | } |
100 | } |
101 | |
102 | /** |
103 | * @param array $params |
104 | * @return Coord|null |
105 | */ |
106 | private function getFromCoord( array $params ): ?Coord { |
107 | $this->requireMaxOneParameter( $params, 'distancefrompoint', 'distancefrompage' ); |
108 | $globe = new Globe( 'earth' ); |
109 | if ( $params['distancefrompoint'] !== null ) { |
110 | $arr = explode( '|', $params['distancefrompoint'] ); |
111 | if ( count( $arr ) != 2 || !$globe->coordinatesAreValid( $arr[0], $arr[1] ) ) { |
112 | $this->dieWithError( 'apierror-geodata-badcoord', 'invalid-coord' ); |
113 | } |
114 | return new Coord( (float)$arr[0], (float)$arr[1], 'earth' ); |
115 | } |
116 | if ( $params['distancefrompage'] !== null ) { |
117 | $title = Title::newFromText( $params['distancefrompage'] ); |
118 | if ( !$title || !$title->exists() ) { |
119 | $this->dieWithError( [ |
120 | 'apierror-invalidtitle', |
121 | wfEscapeWikiText( $params['distancefrompage'] ) |
122 | ] ); |
123 | } |
124 | // @phan-suppress-next-line PhanTypeMismatchArgumentNullable T240141 |
125 | $page = $this->wikiPageFactory->newFromTitle( $title ); |
126 | $redirectTarget = $page->getRedirectTarget(); |
127 | if ( $redirectTarget ) { |
128 | $title = $redirectTarget; |
129 | } |
130 | $coord = GeoData::getPageCoordinates( $title->getArticleID() ); |
131 | if ( !$coord ) { |
132 | $this->dieWithError( |
133 | [ |
134 | 'apierror-geodata-noprimarycoord', |
135 | wfEscapeWikiText( $title->getPrefixedText() ) |
136 | ], |
137 | 'no-coordinates' |
138 | ); |
139 | } |
140 | if ( $coord->globe != 'earth' ) { |
141 | $this->dieWithError( 'apierror-geodata-notonearth', 'notonearth' ); |
142 | } |
143 | return $coord; |
144 | } |
145 | return null; |
146 | } |
147 | |
148 | /** @inheritDoc */ |
149 | public function getCacheMode( $params ) { |
150 | return 'public'; |
151 | } |
152 | |
153 | /** @inheritDoc */ |
154 | public function getAllowedParams() { |
155 | return [ |
156 | 'limit' => [ |
157 | ParamValidator::PARAM_DEFAULT => 10, |
158 | ParamValidator::PARAM_TYPE => 'limit', |
159 | IntegerDef::PARAM_MIN => 1, |
160 | IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1, |
161 | IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2 |
162 | ], |
163 | 'continue' => [ |
164 | ParamValidator::PARAM_TYPE => 'string', |
165 | ApiBase::PARAM_HELP_MSG => 'api-help-param-continue', |
166 | ], |
167 | 'prop' => [ |
168 | ParamValidator::PARAM_TYPE => [ 'type', 'name', 'dim', 'country', 'region', 'globe' ], |
169 | ParamValidator::PARAM_DEFAULT => 'globe', |
170 | ParamValidator::PARAM_ISMULTI => true, |
171 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [], |
172 | ], |
173 | 'primary' => [ |
174 | ParamValidator::PARAM_TYPE => [ 'primary', 'secondary', 'all' ], |
175 | ParamValidator::PARAM_DEFAULT => 'primary', |
176 | ApiBase::PARAM_HELP_MSG_PER_VALUE => [], |
177 | ], |
178 | 'distancefrompoint' => [ |
179 | ParamValidator::PARAM_TYPE => 'string', |
180 | ApiBase::PARAM_HELP_MSG_APPEND => [ |
181 | 'geodata-api-help-coordinates-format', |
182 | ], |
183 | ], |
184 | 'distancefrompage' => [ |
185 | ParamValidator::PARAM_TYPE => 'string', |
186 | ], |
187 | ]; |
188 | } |
189 | |
190 | /** @inheritDoc */ |
191 | protected function getExamplesMessages() { |
192 | return [ |
193 | 'action=query&prop=coordinates&titles=Main%20Page' |
194 | => 'apihelp-query+coordinates-example-1', |
195 | ]; |
196 | } |
197 | |
198 | /** @inheritDoc */ |
199 | public function getHelpUrls() { |
200 | return 'https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:GeoData#prop.3Dcoordinates'; |
201 | } |
202 | } |