jenkins-bot has submitted this change and it was merged. Change subject: Static map page for non-JS support of maplinks ......................................................................
Static map page for non-JS support of maplinks Special:Map/{zoom}/{latitude}/{longitude} Bug: T144003 Change-Id: I90fcd2fb0e458c9d5a805b441f5114157ea470a4 --- A Kartographer.alias.php M extension.json M i18n/en.json M i18n/qqq.json A includes/Projection/EPSG3857.php A includes/Projection/SphericalMercator.php A includes/Projection/Transformation.php A includes/SpecialMap.php M includes/Tag/MapLink.php A styles/specialMap.less M tests/parserTests.txt A tests/phpunit/SpecialMapTest.php 12 files changed, 474 insertions(+), 24 deletions(-) Approvals: Yurik: Looks good to me, approved jenkins-bot: Verified diff --git a/Kartographer.alias.php b/Kartographer.alias.php new file mode 100644 index 0000000..c788e1a --- /dev/null +++ b/Kartographer.alias.php @@ -0,0 +1,15 @@ +<?php +/** + * Aliases for special pages for extension Echo + * + * @file + * @ingroup Extensions + */ +// @codingStandardsIgnoreFile + +$specialPageAliases = array(); + +/** English (English) */ +$specialPageAliases['en'] = [ + 'Map' => [ 'Map' ], +]; diff --git a/extension.json b/extension.json index 0af590a..ea345dd 100644 --- a/extension.json +++ b/extension.json @@ -16,6 +16,12 @@ "modules/wikivoyage/i18n" ] }, + "ExtensionMessagesFiles": { + "KartogrpaherAliases": "Kartographer.alias.php" + }, + "SpecialPages": { + "Map": "Kartographer\\SpecialMap" + }, "AutoloadClasses": { "Kartographer\\ApiQueryMapData": "includes/ApiQueryMapData.php", "Kartographer\\ApiSanitizeMapData": "includes/ApiSanitizeMapData.php", @@ -23,10 +29,14 @@ "Kartographer\\DataModule": "includes/DataModule.php", "Kartographer\\Hooks": "includes/Hooks.php", "Kartographer\\SimpleStyleParser": "includes/SimpleStyleParser.php", + "Kartographer\\SpecialMap": "includes/SpecialMap.php", "Kartographer\\State": "includes/State.php", "Kartographer\\Tag\\MapFrame": "includes/Tag/MapFrame.php", "Kartographer\\Tag\\MapLink": "includes/Tag/MapLink.php", - "Kartographer\\Tag\\TagHandler": "includes/Tag/TagHandler.php" + "Kartographer\\Tag\\TagHandler": "includes/Tag/TagHandler.php", + "Kartographer\\Projection\\EPSG3857": "includes/Projection/EPSG3857.php", + "Kartographer\\Projection\\SphericalMercator": "includes/Projection/SphericalMercator.php", + "Kartographer\\Projection\\Transformation": "includes/Projection/Transformation.php" }, "APIModules": { "sanitize-mapdata": "Kartographer\\ApiSanitizeMapData" @@ -324,6 +334,15 @@ "mobile", "desktop" ] + }, + "ext.kartographer.specialMap": { + "styles": [ + "styles/specialMap.less" + ], + "targets": [ + "mobile", + "desktop" + ] } }, "ResourceFileModulePaths": { diff --git a/i18n/en.json b/i18n/en.json index 6249d19..7740904 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -33,6 +33,7 @@ "kartographer-coord-lat-negative": "$1S", "kartographer-coord-lon-positive": "$1E", "kartographer-coord-lon-negative": "$1W", + "kartographer-specialmap-invalid-coordinates": "Invalid coordinates supplied", "kartographer.css": "/* CSS placed here will be applied to all pages with maps */", "kartographer.js": "/* Any JavaScript here will be loaded for all users on load of the map-containing pages */", "leafletdraw-draw-handlers-circle-radius": "Radius", @@ -67,6 +68,7 @@ "leafletdraw-edit-toolbar-buttons-editdisabled": "No layers to edit", "leafletdraw-edit-toolbar-buttons-remove": "Delete layers", "leafletdraw-edit-toolbar-buttons-removedisabled": "No layers to delete", + "map": "Coordinates information", "mapbox-control-zoomin-title": "Zoom in", "mapbox-control-zoomout-title": "Zoom out", "kartographer-fullscreen-close": "Close", diff --git a/i18n/qqq.json b/i18n/qqq.json index 92b856c..1c1e886 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -35,7 +35,8 @@ "kartographer-coord-lat-positive": "{{optional}}\nNorth latitude suffix for geographical coordinates. $1 is value in degrees/minutes/seconds, see {{msg-mw|kartographer-coord-dms}}", "kartographer-coord-lat-negative": "{{optional}}\nSouth latitude suffix for geographical coordinates. $1 is value in degrees/minutes/seconds, see {{msg-mw|kartographer-coord-dms}}", "kartographer-coord-lon-positive": "{{optional}}\nEast longitude suffix for geographical coordinates. $1 is value in degrees/minutes/seconds, see {{msg-mw|kartographer-coord-dms}}", - "kartographer-coord-lon-negative": "{{optional}}\nWest longitude suffix for geographical coordinates. $1 is value in degrees/minutes/seconds, see {{msg-mw|kartographer-coord-dms}}", + "kartographer-coord-lon-negative": "{`{optional}}\nWest longitude suffix for geographical coordinates. $1 is value in degrees/minutes/seconds, see {{msg-mw|kartographer-coord-dms}}", + "kartographer-specialmap-invalid-coordinates": "Displayed by Special:Map when no valid coordinates are supplied", "kartographer.css": "{{optional}}", "kartographer.js": "{{optional}}", "leafletdraw-draw-handlers-circle-radius": "Label for circle radius\n{{Identical|Radius}}", @@ -70,6 +71,7 @@ "leafletdraw-edit-toolbar-buttons-editdisabled": "Tooltip for edit layers button when disabled", "leafletdraw-edit-toolbar-buttons-remove": "Tooltip for delete layers button", "leafletdraw-edit-toolbar-buttons-removedisabled": "Tooltip for delete layers button when disabled", + "map": "Page title for Special:Map", "mapbox-control-zoomin-title": "Title for map zoom in button\n{{Identical|Zoom in}}", "mapbox-control-zoomout-title": "Title for map zoom out button\n{{Identical|Zoom out}}", "kartographer-fullscreen-close": "Title of the fullscreen close button\n{{Identical|Close}}", diff --git a/includes/Projection/EPSG3857.php b/includes/Projection/EPSG3857.php new file mode 100644 index 0000000..5303f38 --- /dev/null +++ b/includes/Projection/EPSG3857.php @@ -0,0 +1,64 @@ +<?php + +namespace Kartographer\Projection; + +/** + * EPSG3857 (Spherical Mercator) is the most common CRS for web mapping + * and is used by Leaflet by default. + * + * Converted to PHP from L.CRS.EPSG3857 (leaflet.js) + */ +class EPSG3857 { + + const EARTH_RADIUS = 6378137; + + /** + * (LatLon) -> Point + * + * @param float[] $latLon + * @return float[] + */ + public static function project( $latLon ) { + $projectedPoint = SphericalMercator::project( $latLon ); + + return [ $projectedPoint * self::EARTH_RADIUS, $projectedPoint * self::EARTH_RADIUS ]; + } + + /** + * (LatLon, Number) -> Point + * + * @param float[] $latLon + * @param int $zoom + * @return array + */ + public static function latLonToPoint( $latLon, $zoom ) { + $projectedPoint = SphericalMercator::project( $latLon ); + $scale = self::scale( $zoom ); + + return Transformation::transform( $projectedPoint, $scale ); + } + + /** + * (Point, Number[, Boolean]) -> LatLon + * + * @param $point + * @param $zoom + * @return array + */ + public static function pointToLatLon( $point, $zoom ) { + $scale = self::scale( $zoom ); + $untransformedPoint = Transformation::untransform( $point, $scale ); + + return SphericalMercator::unproject( $untransformedPoint ); + } + + public static function scale( $zoom ) { + return 256 * pow( 2, $zoom ); + } + + public static function getSize( $zoom ) { + $size = self::scale( $zoom ); + + return [ $size, $size ]; + } +} diff --git a/includes/Projection/SphericalMercator.php b/includes/Projection/SphericalMercator.php new file mode 100644 index 0000000..33d9c48 --- /dev/null +++ b/includes/Projection/SphericalMercator.php @@ -0,0 +1,44 @@ +<?php + +namespace Kartographer\Projection; + +/** + * Spherical Mercator is the most popular map projection, + * used by EPSG:3857 CRS. + * + * Converted to PHP from L.Projection.SphericalMercator (leaflet.js) + */ +class SphericalMercator { + + const MAX_LATITUDE = 85.0511287798; + + /** + * (LatLon) -> Point + * + * @param float[] $latLon + * @return array + */ + public static function project( $latLon ) { + + $lat = max( min( self::MAX_LATITUDE, $latLon[0] ), -self::MAX_LATITUDE ); + $x = deg2rad( $latLon[1] ); + $y = deg2rad( $lat ); + + $y = log( tan( ( pi() / 4 ) + ( $y / 2 ) ) ); + + return [ $x, $y ]; + } + + /** + * (Point, Boolean) -> LatLon + * + * @param float[] $point + * @return array + */ + public static function unproject( $point ) { + $lon = rad2deg( $point[0] ); + $lat = rad2deg( 2 * atan( exp( $point[1] ) ) - ( pi() / 2 ) ); + + return [ $lat, $lon ]; + } +} diff --git a/includes/Projection/Transformation.php b/includes/Projection/Transformation.php new file mode 100644 index 0000000..04d8380 --- /dev/null +++ b/includes/Projection/Transformation.php @@ -0,0 +1,43 @@ +<?php + +namespace Kartographer\Projection; + +/** + * Transformation is an utility class to perform simple point transformations + * through a 2d-matrix. + * + * Converted to PHP from L.Transformation (leaflet.js) + */ +class Transformation { +// @fixme: cleanup + const A = 0.159154943; // 0.5 * pi() + const C = -0.159154943; // -0.5 * pi() + + /** + * (LatLon) -> Point + * + * @param float[] $point + * @param int $scale + * @return float[] + */ + public static function transform( $point, $scale = 1 ) { + + $x = $point[0]; + $y = $point[1]; + + $x = $scale * ( self::A * $x + 0.5); + $y = $scale * ( self::C * $y + 0.5 ); + + return [ $x, $y ]; + } + + public static function untransform( $point, $scale = 1 ) { + $x = $point[0]; + $y = $point[1]; + + $x = ( $x / $scale - 0.5 ) / self::A; + $y = ( $y / $scale - 0.5 ) / self::C; + + return [ $x, $y ]; + } +} diff --git a/includes/SpecialMap.php b/includes/SpecialMap.php new file mode 100644 index 0000000..651e7a1 --- /dev/null +++ b/includes/SpecialMap.php @@ -0,0 +1,95 @@ +<?php + +namespace Kartographer; + +use GeoData\Globe; +use Html; +use SpecialPage; +use Title; +use Kartographer\Projection\EPSG3857; + +/** + * Special page that works as a fallback destination for non-JS users + * who click on map links. It displays a world map with a dot for the given location. + * URL format: Special:Map/<zoom>/<lat>/<lon> + * Zoom isn't used anywhere yet. + */ +class SpecialMap extends SpecialPage { + public function __construct( $name = 'Map' ) { + parent::__construct( $name, /* $restriction */ '', /* $listed */ false ); + } + + public function execute( $par ) { + $this->setHeaders(); + $this->getOutput()->addModuleStyles( 'ext.kartographer.specialMap' ); + + $coord = self::parseSubpage( $par ); + if ( !$coord ) { + $coordText = wfMessage( 'kartographer-specialmap-invalid-coordinates' )->text(); + $markerHtml = ''; + } else { + list( , $lat, $lon ) = $coord; + $coordText = CoordFormatter::format( $lat, $lon, $this->getLanguage() ); + list( $x, $y ) = EPSG3857::latLonToPoint( [ $lat, $lon ], 0 ); + $markerHtml = Html::element( 'div', + [ + 'id' => 'mw-specialMap-marker', + 'style' => "left:{$x}px; top:{$y}px;" + ] + ); + } + + $attributions = Html::rawElement( 'div', [ 'id' => 'mw-specialMap-attributions' ], + wfMessage( 'kartographer-attribution' )->parse() ); + + $this->getOutput()->addHTML( + Html::openElement( 'div', [ 'id' => 'mw-specialMap-container', 'class' => 'thumb' ] ) + . Html::openElement( 'div', [ 'class' => 'thumbinner' ] ) + . Html::openElement( 'div', [ 'id' => 'mw-specialMap-inner' ] ) + . Html::element( 'div', [ 'id' => 'mw-specialMap-map' ] ) + . $markerHtml + . $attributions + . Html::closeElement( 'div' ) + . Html::openElement( 'div', [ 'id' => 'mw-specialMap-caption', 'class' => 'thumbcaption' ] ) + . Html::element( 'span', [ 'id' => 'mw-specialMap-icon' ] ) + . Html::element( 'span', [ 'id' => 'mw-specialMap-coords' ], $coordText ) + . Html::closeElement( 'div' ) + . Html::closeElement( 'div' ) + . Html::closeElement( 'div' ) + ); + } + + /** + * Parses subpage parameter to this special page into zoom / lat /lon + * + * @param $par + * @return array|bool + */ + public static function parseSubpage( $par ) { + if ( !preg_match( '#^(?<zoom>\d+)/(?<lat>-?\d+(\.\d+)?)/(?<lon>-?\d+(\.\d+)?)$#', $par, $matches ) ) { + return false; + } + + if ( class_exists( Globe::class ) ) { + $globe = new Globe( 'earth' ); + + if ( !$globe->coordinatesAreValid( $matches['lat'], $matches['lon'] ) ) { + return false; + } + } + + return [ (int)$matches['zoom'], (float)$matches['lat'], (float)$matches['lon'] ]; + } + + /** + * Returns a Title for a link to the coordinates provided + * + * @param float $lat + * @param float $lon + * @param int $zoom + * @return Title + */ + public static function link( $lat, $lon, $zoom ) { + return SpecialPage::getTitleFor( 'Map', "$zoom/$lat/$lon" ); + } +} diff --git a/includes/Tag/MapLink.php b/includes/Tag/MapLink.php index f11c667..a55a5ca 100644 --- a/includes/Tag/MapLink.php +++ b/includes/Tag/MapLink.php @@ -5,6 +5,7 @@ use FormatJson; use Html; use Kartographer\CoordFormatter; +use Kartographer\SpecialMap; /** * The <maplink> tag creates a link that, when clicked, @@ -35,6 +36,7 @@ 'class' => 'mw-kartographer-maplink', 'mw-data' => 'interface', 'data-style' => $this->mapStyle, + 'href' => SpecialMap::link( $this->lat, $this->lon, $this->zoom )->getLocalURL() ]; if ( $this->zoom !== null ) { $attrs['data-zoom'] = $this->zoom; diff --git a/styles/specialMap.less b/styles/specialMap.less new file mode 100644 index 0000000..f2cb16c --- /dev/null +++ b/styles/specialMap.less @@ -0,0 +1,92 @@ +@mapWidth: 256px; +@mapHeight: 256px; +@markerColor: #7b4e10; +@markerSize: 10px; + +/* stylelint-disable selector-no-id */ + +div#mw-specialMap-container { + + position: relative; + + .thumbinner { + display: inline-block; + position: relative; + width: @mapWidth; + } +} + +div#mw-specialMap-inner { + height: @mapHeight; + position: relative; +} + +div#mw-specialMap-map { + position: absolute; + width: @mapWidth; + height: @mapHeight; + /* stylelint-disable function-parentheses-space-inside */ + background-image: url(//maps.wikimedia.org/osm-intl/0/0/0.png); + /* stylelint-enable function-parentheses-space-inside */ + background-position: center; + background-repeat: no-repeat; + background-size: @mapWidth @mapHeight; +} + +/* stylelint-disable media-feature-name-no-vendor-prefix */ +@media ( -webkit-min-device-pixel-ratio: 1.5 ), + ( min--moz-device-pixel-ratio: 1.5 ), + ( min-resolution: 1.5dppx ), + ( min-resolution: 144dpi ) { + div#mw-specialMap-map { + /* stylelint-disable function-parentheses-space-inside */ + background-image: url(//maps.wikimedia.org/osm-intl/0/0/0...@1.5x.png); + /* stylelint-enable function-parentheses-space-inside */ + } +} + +@media ( -webkit-min-device-pixel-ratio: 2 ), + ( min--moz-device-pixel-ratio: 2 ), + ( min-resolution: 2dppx ), + ( min-resolution: 192dpi ) { + div#mw-specialMap-map { + /* stylelint-disable function-parentheses-space-inside */ + background-image: url(//maps.wikimedia.org/osm-intl/0/0/0...@2x.png); + /* stylelint-enable function-parentheses-space-inside */ + } +} +/* stylelint-enable media-feature-name-no-vendor-prefix */ + +div#mw-specialMap-marker { + position: absolute; + background-color: @markerColor; + height: @markerSize; + width: @markerSize; + margin-top: -1 * (@markerSize/2); + margin-left: -1 * (@markerSize/2); + border-radius: @markerSize/2; +} + +div#mw-specialMap-caption { + display: block; + padding-top: 0.5em; +} + +span#mw-specialMap-icon { + display: inline-block; + margin: 0 12px 0 0; + background-color: @markerColor; + height: @markerSize; + width: @markerSize; + margin-top: -1 * (@markerSize/2); + margin-left: 0.5em; + border-radius: @markerSize/2; +} + +div#mw-specialMap-attributions { + position: absolute; + bottom: 0; + font-size: 9px; + right: 0; + text-align: right; +} diff --git a/tests/parserTests.txt b/tests/parserTests.txt index 856ed8d..0448d48 100644 --- a/tests/parserTests.txt +++ b/tests/parserTests.txt @@ -4,8 +4,8 @@ <maplink /> <maplink latitude=10 longitude=20 zoom=13 text='Foo & bar'/> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl">0°0′0″N 0°0′0″E</a> -<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20">Foo & bar</a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map///">0°0′0″N 0°0′0″E</a> +<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" data-lon="20">Foo & bar</a> </p> !! end @@ -14,14 +14,14 @@ !! input <maplink latitude=10 longitude=20 zoom=13 text='<&'/> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20"><&</a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" data-lon="20"><&</a> </p> !! end !! test <maplink> - counters and markers !! input -<maplink latitude=10 longitude=20 zoom=13 text='Foo'> +<maplink latitude=10.123 longitude=20.456 zoom=13 text='Foo'> { "type": "Feature", "geometry": { @@ -73,10 +73,10 @@ } </maplink> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20" data-overlays="["_b34053f88dea58fe70a7da09c558f792394f6e11"]">Foo</a> -</p><p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20" data-overlays="["_2e4133dc429eb3c3abf4e45916c073e841f9d193"]">2</a> -</p><p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20" data-overlays="["_dd3c7bae539f7c3747c64d043c97e1a8834fbc8f"]">A</a> -</p><p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm" data-zoom="13" data-lat="10" data-lon="20" data-overlays="["_8ce73f7395e427bd6f462824717c0a214b3ca564"]">B</a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/13/10.123/20.456" data-zoom="13" data-lat="10.123" data-lon="20.456" data-overlays="["_b34053f88dea58fe70a7da09c558f792394f6e11"]">Foo</a> +</p><p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" data-lon="20" data-overlays="["_2e4133dc429eb3c3abf4e45916c073e841f9d193"]">2</a> +</p><p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" data-lon="20" data-overlays="["_dd3c7bae539f7c3747c64d043c97e1a8834fbc8f"]">A</a> +</p><p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm" href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" data-lon="20" data-overlays="["_8ce73f7395e427bd6f462824717c0a214b3ca564"]">B</a> </p> !! end @@ -85,7 +85,7 @@ !! input <maplink latitude=10 longitude=20 zoom=13 /> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20">10°0′0″N 20°0′0″E</a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" data-lon="20">10°0′0″N 20°0′0″E</a> </p> !! end @@ -99,9 +99,9 @@ <maplink zoom=0 latitude=0 longitude=0 class="class1 class2" text="Multiple classes aren't allowed for now"/> <maplink zoom=0 latitude=0 longitude=0 class="-Invalid-cl@ss-symbols"/> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0">Empty class - do nothing</a> -<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0">Whitespace only class - do nothing</a> -<a class="mw-kartographer-maplink Valid-link_class_123" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0">Valid class</a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0">Empty class - do nothing</a> +<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0">Whitespace only class - do nothing</a> +<a class="mw-kartographer-maplink Valid-link_class_123" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0">Valid class</a> </p> <div class="mw-kartographer-error"><maplink>: Attribute "class" has an invalid value</div> <div class="mw-kartographer-error"><maplink>: Attribute "class" has an invalid value</div> @@ -228,7 +228,7 @@ !! input <maplink latitude=10 longitude=20 zoom=10 style="color: red;" text="<span style='foo: bar; background-image: url(https://example.com);'>foo</span>"/><!-- style=... was removed --> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="10" data-lat="10" data-lon="20"><span style="/* insecure input */">foo</span></a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/10/10/20" data-zoom="10" data-lat="10" data-lon="20"><span style="/* insecure input */">foo</span></a> </p> !! end @@ -269,7 +269,7 @@ } ]</maplink> !! result -<p><a class="mw-kartographer-maplink mw-kartographer-autostyled" mw-data="interface" data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20" style="background: #abcdef;" data-overlays="["_0d28e3b8954f29b4a6af62bd43a9cacc70ce0261"]">A</a> +<p><a class="mw-kartographer-maplink mw-kartographer-autostyled" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" data-lon="20" style="background: #abcdef;" data-overlays="["_0d28e3b8954f29b4a6af62bd43a9cacc70ce0261"]">A</a> </p> !! end @@ -292,7 +292,7 @@ } </maplink> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="13" data-lat="10" data-lon="20" data-overlays="["_a724df91b09b20c8c7ea0dc28127eaed1727511b"]">A</a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/13/10/20" data-zoom="13" data-lat="10" data-lon="20" data-overlays="["_a724df91b09b20c8c7ea0dc28127eaed1727511b"]">A</a> </p> !! end @@ -316,7 +316,7 @@ } </mapframe> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["ponies"]">0°0′0″N 0°0′0″E</a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["ponies"]">0°0′0″N 0°0′0″E</a> </p> <div class="mw-kartographer-container thumb tright"><div class="thumbinner" style="width: 300px;"><div class="mw-kartographer-map" mw-data="interface" data-style="osm-intl" data-width="300" data-height="300" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["ponies"]" style="height: 300px;"></div><div class="thumbcaption"></div></div></div> @@ -342,7 +342,7 @@ } </mapframe> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 0°0′0″E</a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 0°0′0″E</a> </p> <div class="mw-kartographer-container thumb tright"><div class="thumbinner" style="width: 300px;"><div class="mw-kartographer-map" mw-data="interface" data-style="osm-intl" data-width="300" data-height="300" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["_a724df91b09b20c8c7ea0dc28127eaed1727511b"]" style="height: 300px;"></div><div class="thumbcaption"></div></div></div> @@ -360,11 +360,11 @@ <maplink zoom=0 latitude=0 longitude=0 show=""/> <maplink zoom=0 latitude=0 longitude=0 show='йа криветко'/> !! result -<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["foo"]">0°0′0″N 0°0′0″E</a> -<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["foo","bar"]">0°0′0″N 0°0′0″E</a> -<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["foo"]">0°0′0″N 0°0′0″E</a> -<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 0°0′0″E</a> -<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 0°0′0″E</a> +<p><a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["foo"]">0°0′0″N 0°0′0″E</a> +<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["foo","bar"]">0°0′0″N 0°0′0″E</a> +<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0" data-overlays="["foo"]">0°0′0″N 0°0′0″E</a> +<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 0°0′0″E</a> +<a class="mw-kartographer-maplink" mw-data="interface" data-style="osm-intl" href="/wiki/Special:Map/0/0/0" data-zoom="0" data-lat="0" data-lon="0">0°0′0″N 0°0′0″E</a> </p> <div class="mw-kartographer-error"><maplink>: Attribute "show" has an invalid value</div> diff --git a/tests/phpunit/SpecialMapTest.php b/tests/phpunit/SpecialMapTest.php new file mode 100644 index 0000000..4f2c79a --- /dev/null +++ b/tests/phpunit/SpecialMapTest.php @@ -0,0 +1,72 @@ +<?php + +namespace Kartographer\Tests; + +use GeoData\Globe; +use Kartographer\SpecialMap; +use MediaWikiTestCase; +use Title; + +/** + * @group Kartographer + */ +class SpecialMapTest extends MediaWikiTestCase { + + /** + * @dataProvider provideParseSubpage + * + * @param string $par + * @param float $expectedLat + * @param float $expectedLon + */ + public function testParseSubpage( $par, $expectedLat = null, $expectedLon = null ) { + $res = SpecialMap::parseSubpage( $par ); + if ( $expectedLat === null || $expectedLon === null ) { + $this->assertFalse( $res, 'Parsing is expected to fail' ); + } else { + list( , $lat, $lon ) = $res; + $this->assertSame( $expectedLat, $lat, 'Comparing latitudes' ); + $this->assertSame( $expectedLon, $lon, 'Comparing longitudes' ); + } + } + + public function provideParseSubpage() { + $tests = [ + [ '' ], + [ 'foo' ], + [ 'foo/bar/baz' ], + [ '123' ], + [ '1/2' ], + [ '1/2/3/4' ], + [ '1.0/2/3' ], + [ '-1/2/3' ], + [ '1/2/.3' ], + [ '1/2/3.45e+6' ], + [ '1/2,3/4,5' ], + + [ '0/0/-0', 0.0, 0.0 ], + [ '12/-34.56/0.78', -34.56, 0.78 ], + [ '18/89.9/179.9', 89.9, 179.9 ], + [ '18/-89.9/-179.9', -89.9, -179.9 ], + [ '18/90/-180', 90.0, -180.0 ], + ]; + + if ( class_exists( Globe::class ) ) { + $tests = array_merge( $tests, + [ + [ '0/90.000001/10' ], + [ '0/10/180.000000001' ], + ] + ); + } + + return $tests; + } + + public function testLink() { + $this->setMwGlobals( 'wgArticlePath', '/wiki/$1' ); + $title = SpecialMap::link( 12, -34.5, 6 ); + $this->assertType( Title::class, $title ); + $this->assertEquals( '/wiki/Special:Map/6/12/-34.5', $title->getLocalURL() ); + } +} -- To view, visit https://gerrit.wikimedia.org/r/306315 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I90fcd2fb0e458c9d5a805b441f5114157ea470a4 Gerrit-PatchSet: 9 Gerrit-Project: mediawiki/extensions/Kartographer Gerrit-Branch: master Gerrit-Owner: MaxSem <maxsem.w...@gmail.com> Gerrit-Reviewer: JGirault <julien.inbox.w...@gmail.com> Gerrit-Reviewer: MaxSem <maxsem.w...@gmail.com> Gerrit-Reviewer: Yurik <yu...@wikimedia.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits