Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 178 |
|
0.00% |
0 / 8 |
CRAP | |
0.00% |
0 / 1 |
CargoMapsFormat | |
0.00% |
0 / 178 |
|
0.00% |
0 / 8 |
5112 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
allowedParameters | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
getScripts | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
getStyles | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getImageURL | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
getImageData | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
display | |
0.00% |
0 / 121 |
|
0.00% |
0 / 1 |
2652 | |||
getMapPointValues | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
42 |
1 | <?php |
2 | /** |
3 | * @author Yaron Koren |
4 | * @ingroup Cargo |
5 | */ |
6 | |
7 | class CargoMapsFormat extends CargoDisplayFormat { |
8 | |
9 | public static $mappingService = "OpenLayers"; |
10 | public static $mapNumber = 1; |
11 | |
12 | public function __construct( $output ) { |
13 | global $wgCargoDefaultMapService; |
14 | parent::__construct( $output ); |
15 | self::$mappingService = $wgCargoDefaultMapService; |
16 | } |
17 | |
18 | public static function allowedParameters() { |
19 | return [ |
20 | 'height' => [ 'type' => 'int', 'label' => wfMessage( 'cargo-viewdata-heightparam' )->parse() ], |
21 | 'width' => [ 'type' => 'int', 'label' => wfMessage( 'cargo-viewdata-widthparam' )->parse() ], |
22 | 'icon' => [ 'type' => 'string' ], |
23 | 'zoom' => [ 'type' => 'int' ], |
24 | 'center' => [ 'type' => 'string' ], |
25 | 'cluster' => [ 'type' => 'string' ] |
26 | ]; |
27 | } |
28 | |
29 | public static function getScripts() { |
30 | global $wgCargoDefaultMapService; |
31 | if ( $wgCargoDefaultMapService == 'Google Maps' ) { |
32 | return CargoGoogleMapsFormat::getScripts(); |
33 | } elseif ( $wgCargoDefaultMapService == 'OpenLayers' ) { |
34 | return CargoOpenLayersFormat::getScripts(); |
35 | } elseif ( $wgCargoDefaultMapService == 'Leaflet' ) { |
36 | return CargoLeafletFormat::getScripts(); |
37 | } else { |
38 | return []; |
39 | } |
40 | } |
41 | |
42 | public static function getStyles() { |
43 | global $wgCargoDefaultMapService; |
44 | if ( $wgCargoDefaultMapService == 'Leaflet' ) { |
45 | return CargoLeafletFormat::getStyles(); |
46 | } else { |
47 | return []; |
48 | } |
49 | } |
50 | |
51 | /** |
52 | * Based on the Maps extension's getFileUrl(). |
53 | */ |
54 | public static function getImageURL( $imageName ) { |
55 | $title = Title::makeTitle( NS_FILE, $imageName ); |
56 | |
57 | if ( $title == null || !$title->exists() ) { |
58 | return null; |
59 | } |
60 | |
61 | $imagePage = new ImagePage( $title ); |
62 | return $imagePage->getDisplayedFile()->getURL(); |
63 | } |
64 | |
65 | public function getImageData( $fileName ) { |
66 | global $wgUploadDirectory; |
67 | |
68 | if ( $fileName == '' ) { |
69 | return null; |
70 | } |
71 | $fileTitle = Title::makeTitleSafe( NS_FILE, $fileName ); |
72 | if ( !$fileTitle->exists() ) { |
73 | throw new MWException( "Error: File \"$fileName\" does not exist on this wiki." ); |
74 | } |
75 | $imagePage = new ImagePage( $fileTitle ); |
76 | $file = $imagePage->getDisplayedFile(); |
77 | $filePath = $wgUploadDirectory . '/' . $file->getUrlRel(); |
78 | [ $imageWidth, $imageHeight, $type, $attr ] = getimagesize( $filePath ); |
79 | return [ $imageWidth, $imageHeight, $file->getUrl() ]; |
80 | } |
81 | |
82 | /** |
83 | * @param array $valuesTable |
84 | * @param array $formattedValuesTable |
85 | * @param array $fieldDescriptions |
86 | * @param array $displayParams |
87 | * @return string HTML |
88 | * @throws MWException |
89 | */ |
90 | public function display( $valuesTable, $formattedValuesTable, $fieldDescriptions, $displayParams ) { |
91 | $coordinatesFields = []; |
92 | $latField = null; |
93 | $lonField = null; |
94 | $urlField = null; |
95 | foreach ( $fieldDescriptions as $field => $description ) { |
96 | if ( $description->mType == 'Coordinates' ) { |
97 | $coordinatesFields[] = $field; |
98 | } |
99 | if ( $field == 'lat' ) { |
100 | $latField = $field; |
101 | } elseif ( $field == 'lon' ) { |
102 | $lonField = $field; |
103 | } elseif ( $field == 'URL' ) { |
104 | $urlField = $field; |
105 | } |
106 | } |
107 | |
108 | if ( count( $coordinatesFields ) == 0 && ( $latField == null || $lonField == null ) ) { |
109 | throw new MWException( "Error: no fields of type \"Coordinates\" were specified in this " |
110 | . "query; cannot display in a map." ); |
111 | } |
112 | |
113 | // @TODO - should this check be higher up, i.e. for all |
114 | // formats? |
115 | if ( count( $formattedValuesTable ) == 0 ) { |
116 | throw new MWException( "No results found for this query; not displaying a map." ); |
117 | } |
118 | |
119 | // Add necessary JS scripts and CSS styles. |
120 | $scripts = $this->getScripts(); |
121 | $scriptsHTML = ''; |
122 | foreach ( $scripts as $script ) { |
123 | $scriptsHTML .= Html::linkedScript( $script ); |
124 | } |
125 | $styles = $this->getStyles(); |
126 | $stylesHTML = ''; |
127 | foreach ( $styles as $style ) { |
128 | $stylesHTML .= Html::linkedStyle( $style ); |
129 | } |
130 | $this->mOutput->addHeadItem( $scriptsHTML, $scriptsHTML ); |
131 | $this->mOutput->addHeadItem( $stylesHTML, $stylesHTML ); |
132 | $this->mOutput->addModules( [ 'ext.cargo.maps' ] ); |
133 | |
134 | // Construct the table of data we will display. |
135 | $valuesForMap = []; |
136 | foreach ( $formattedValuesTable as $i => $valuesRow ) { |
137 | $displayedValuesForRow = []; |
138 | foreach ( $valuesRow as $fieldName => $fieldValue ) { |
139 | if ( !array_key_exists( $fieldName, $fieldDescriptions ) ) { |
140 | continue; |
141 | } |
142 | $fieldType = $fieldDescriptions[$fieldName]->mType; |
143 | // Don't display any values that are going to be included already. |
144 | if ( $fieldType == 'Coordinates' || $fieldType == 'Coordinates part' || $fieldName == $latField || $fieldName == $lonField || $fieldName == $urlField ) { |
145 | continue; |
146 | } |
147 | if ( $fieldValue == '' ) { |
148 | continue; |
149 | } |
150 | $displayedValuesForRow[$fieldName] = $fieldValue; |
151 | } |
152 | // There could potentially be more than one |
153 | // coordinate for this "row". |
154 | // @TODO - handle lists of coordinates as well. |
155 | foreach ( $coordinatesFields as $coordinatesField ) { |
156 | $coordinatesField = str_replace( ' ', '_', $coordinatesField ); |
157 | $latValue = $valuesRow[$coordinatesField . ' lat'] ?? null; |
158 | $lonValue = $valuesRow[$coordinatesField . ' lon'] ?? null; |
159 | if ( $latValue != '' && $lonValue != '' ) { |
160 | $nameValue = array_shift( $valuesTable[$i] ); |
161 | // @TODO - enforce the existence of a field |
162 | // besides the coordinates field(s). |
163 | $firstValue = array_shift( $displayedValuesForRow ); |
164 | $valuesForMap[] = self::getMapPointValues( $nameValue, $firstValue, $latValue, $lonValue, $displayedValuesForRow, $displayParams, $i ); |
165 | } |
166 | } |
167 | |
168 | if ( $latField !== null && $lonField !== null ) { |
169 | $latValue = $valuesRow[$latField] ?? null; |
170 | $lonValue = $valuesRow[$lonField] ?? null; |
171 | $urlValue = $valuesRow[$urlField] ?? null; |
172 | if ( $latValue != '' && $lonValue != '' ) { |
173 | $nameValue = array_shift( $valuesTable[$i] ); |
174 | $titleValue = array_shift( $displayedValuesForRow ); |
175 | if ( $urlValue !== null ) { |
176 | $titleValue = Html::element( 'a', [ 'href' => $urlValue ], $titleValue ); |
177 | } |
178 | $valuesForMap[] = self::getMapPointValues( $nameValue, $titleValue, $latValue, $lonValue, $displayedValuesForRow, $displayParams, $i ); |
179 | } |
180 | } |
181 | } |
182 | |
183 | $service = self::$mappingService; |
184 | $jsonData = json_encode( $valuesForMap, JSON_NUMERIC_CHECK | JSON_HEX_TAG ); |
185 | $divID = "mapCanvas" . self::$mapNumber++; |
186 | |
187 | if ( $service == 'Leaflet' && array_key_exists( 'image', $displayParams ) ) { |
188 | $fileName = $displayParams['image']; |
189 | $imageData = $this->getImageData( $fileName ); |
190 | if ( $imageData == null ) { |
191 | $fileName = null; |
192 | } else { |
193 | [ $imageWidth, $imageHeight, $imageURL ] = $imageData; |
194 | } |
195 | } else { |
196 | $fileName = null; |
197 | } |
198 | |
199 | if ( array_key_exists( 'height', $displayParams ) && $displayParams['height'] != '' ) { |
200 | $height = $displayParams['height']; |
201 | // Add on "px", if no unit is defined. |
202 | if ( is_numeric( $height ) ) { |
203 | $height .= "px"; |
204 | } |
205 | } else { |
206 | $height = null; |
207 | } |
208 | |
209 | if ( array_key_exists( 'width', $displayParams ) && $displayParams['width'] != '' ) { |
210 | $width = $displayParams['width']; |
211 | // Add on "px", if no unit is defined. |
212 | if ( is_numeric( $width ) ) { |
213 | $width .= "px"; |
214 | } |
215 | } else { |
216 | $width = null; |
217 | } |
218 | |
219 | if ( $fileName !== null ) { |
220 | // Do some scaling of the image, if necessary. |
221 | if ( $height !== null && $width !== null ) { |
222 | // Reduce image if it doesn't fit into the |
223 | // assigned rectangle. |
224 | $heightRatio = (int)$height / $imageHeight; |
225 | $widthRatio = (int)$width / $imageWidth; |
226 | $smallerRatio = min( $heightRatio, $widthRatio ); |
227 | if ( $smallerRatio < 1 ) { |
228 | $imageHeight *= $smallerRatio; |
229 | $imageWidth *= $smallerRatio; |
230 | } |
231 | } else { |
232 | // Reduce image if it's too big. |
233 | $maxDimension = max( $imageHeight, $imageWidth ); |
234 | $maxAllowedSize = 1000; |
235 | if ( $maxDimension > $maxAllowedSize ) { |
236 | $imageHeight *= $maxAllowedSize / $maxDimension; |
237 | $imageWidth *= $maxAllowedSize / $maxDimension; |
238 | } |
239 | $height = $imageHeight . 'px'; |
240 | $width = $imageWidth . 'px'; |
241 | } |
242 | } else { |
243 | if ( $height == null ) { |
244 | $height = "400px"; |
245 | } |
246 | if ( $width == null ) { |
247 | $width = "700px"; |
248 | } |
249 | } |
250 | |
251 | // The 'map data' element does double duty: it holds the full |
252 | // set of map data, as well as, in the tag attributes, |
253 | // settings related to the display, including the mapping |
254 | // service to use. |
255 | $mapDataAttrs = [ |
256 | 'class' => 'cargoMapData', |
257 | 'style' => 'display: none', |
258 | 'data-mapping-service' => $service |
259 | ]; |
260 | if ( array_key_exists( 'zoom', $displayParams ) && $displayParams['zoom'] != '' ) { |
261 | $mapDataAttrs['data-zoom'] = $displayParams['zoom']; |
262 | } |
263 | if ( array_key_exists( 'center', $displayParams ) && $displayParams['center'] != '' ) { |
264 | $mapDataAttrs['data-center'] = $displayParams['center']; |
265 | } |
266 | if ( array_key_exists( 'cluster', $displayParams ) ) { |
267 | $mapDataAttrs['data-cluster'] = strtolower( $displayParams['cluster'] ); |
268 | } |
269 | if ( $fileName !== null ) { |
270 | $mapDataAttrs['data-image-path'] = $imageURL; |
271 | $mapDataAttrs['data-height'] = $imageHeight; |
272 | $mapDataAttrs['data-width'] = $imageWidth; |
273 | } |
274 | |
275 | $mapData = Html::element( 'span', $mapDataAttrs, $jsonData ); |
276 | |
277 | $mapCanvasAttrs = [ |
278 | 'class' => 'mapCanvas', |
279 | 'style' => "height: $height; width: $width;", |
280 | 'id' => $divID, |
281 | ]; |
282 | $mapCanvas = Html::rawElement( 'div', $mapCanvasAttrs, $mapData ); |
283 | return $mapCanvas; |
284 | } |
285 | |
286 | public static function getMapPointValues( $nameValue, $titleValue, $latValue, $lonValue, $displayedValuesForRow, $displayParams, $rowNum ) { |
287 | $valuesForMapPoint = [ |
288 | // 'name' has no formatting (like a link), while 'title' might. |
289 | 'name' => $nameValue, |
290 | 'title' => $titleValue, |
291 | 'lat' => $latValue, |
292 | 'lon' => $lonValue, |
293 | 'otherValues' => $displayedValuesForRow |
294 | ]; |
295 | if ( array_key_exists( 'icon', $displayParams ) ) { |
296 | $iconFileName = null; |
297 | if ( is_array( $displayParams['icon'] ) ) { |
298 | // Compound query. |
299 | if ( array_key_exists( $rowNum, $displayParams['icon'] ) ) { |
300 | $iconFileName = $displayParams['icon'][$rowNum]; |
301 | } |
302 | } else { |
303 | // Regular query. |
304 | $iconFileName = $displayParams['icon']; |
305 | } |
306 | if ( $iconFileName !== null ) { |
307 | $iconURL = self::getImageURL( $iconFileName ); |
308 | if ( $iconURL !== null ) { |
309 | $valuesForMapPoint['icon'] = $iconURL; |
310 | } |
311 | } |
312 | } |
313 | |
314 | return $valuesForMapPoint; |
315 | } |
316 | |
317 | } |