├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── dependabot.yml ├── workflows │ ├── ci.yml │ └── installMediaWiki.sh └── youtube.jpg ├── .gitignore ├── COPYING ├── DefaultSettings.php ├── INSTALL.md ├── Makefile ├── Maps.php ├── Maps_Settings.php ├── README.md ├── RELEASE-NOTES.md ├── composer.json ├── extension.json ├── i18n ├── af.json ├── aliases │ └── Aliases.php ├── aln.json ├── ar.json ├── arc.json ├── arz.json ├── ast.json ├── az.json ├── ba.json ├── bar.json ├── be-tarask.json ├── bg.json ├── bjn.json ├── bn.json ├── br.json ├── bs.json ├── ca.json ├── ce.json ├── cs.json ├── cu.json ├── cv.json ├── da.json ├── de.json ├── diq.json ├── dsb.json ├── el.json ├── en-gb.json ├── en.json ├── eo.json ├── es.json ├── et.json ├── eu.json ├── fa.json ├── fi.json ├── fr.json ├── frp.json ├── fur.json ├── fy.json ├── ga.json ├── gl.json ├── grc.json ├── gsw.json ├── he.json ├── hi.json ├── hr.json ├── hsb.json ├── ht.json ├── hu.json ├── ia.json ├── id.json ├── ig.json ├── it.json ├── ja.json ├── jv.json ├── ka.json ├── km.json ├── ko.json ├── ksh.json ├── ku-latn.json ├── lad.json ├── lb.json ├── lfn.json ├── lt.json ├── lv.json ├── magic │ └── MagicWords.php ├── map-bms.json ├── mg.json ├── mk.json ├── ml.json ├── mr.json ├── ms.json ├── mt.json ├── myv.json ├── nah.json ├── nb.json ├── nds.json ├── nl-informal.json ├── nl.json ├── nn.json ├── oc.json ├── or.json ├── pa.json ├── pdc.json ├── pfl.json ├── pl.json ├── pms.json ├── ps.json ├── pt-br.json ├── pt.json ├── qqq.json ├── ro.json ├── roa-tara.json ├── ru.json ├── rue.json ├── sco.json ├── sh-latn.json ├── si.json ├── sk.json ├── sl.json ├── sq.json ├── sr-ec.json ├── sr-el.json ├── sv.json ├── sw.json ├── ta.json ├── te.json ├── th.json ├── tl.json ├── tr.json ├── tt-cyrl.json ├── tzm.json ├── uk.json ├── ur.json ├── vep.json ├── vi.json ├── vo.json ├── yi.json ├── zh-hans.json └── zh-hant.json ├── phpcs.xml ├── phpstan-baseline.neon ├── phpstan.neon ├── phpunit.xml.dist ├── psalm-baseline.xml ├── psalm.xml ├── resources ├── GoogleMaps │ ├── ext.maps.googlemaps3.js │ ├── geoxml3 │ │ ├── ProjectedOverlay.js │ │ ├── README │ │ ├── ZipFile.complete.js │ │ └── geoxml3.js │ ├── gm3-util-library │ │ ├── README │ │ ├── markerclusterer.js │ │ ├── markerwithlabel.css │ │ └── markerwithlabel.js │ ├── googlemaps3ajax.js │ ├── img │ │ ├── blue-dot.png │ │ └── mylocation-sprite-2x.png │ ├── jquery.googlemap.js │ └── mylocation.js ├── MapSaver.js ├── WikitextEditor │ ├── css │ │ ├── jquery.miniColors.css │ │ └── mapeditor.css │ ├── images │ │ ├── circle.gif │ │ ├── gradient.png │ │ ├── line.gif │ │ ├── rainbow.png │ │ └── trigger.png │ └── js │ │ ├── README │ │ ├── jquery.miniColors.js │ │ ├── mapeditor.iefixes.js │ │ └── mapeditor.js ├── api.js ├── geoJsonPage.js ├── geojson.new.page.js ├── leaflet │ ├── FeatureBuilder.js │ ├── GeoJson.js │ ├── LeafletCluster.js │ ├── LeafletEditor.js │ ├── LeafletLoader.js │ ├── cluster │ │ ├── m1.png │ │ ├── m2.png │ │ ├── m3.png │ │ ├── m4.png │ │ └── m5.png │ ├── imagePath.js │ ├── images │ │ ├── edit-solid.svg │ │ └── save-solid.svg │ └── jquery.leaflet.js ├── lib │ ├── leaflet-providers │ │ └── leaflet-providers.js │ ├── leaflet.EasyButton │ │ ├── easy-button.css │ │ └── easy-button.js │ ├── leaflet.StyleEditor │ │ ├── css │ │ │ └── Leaflet.StyleEditor.min.css │ │ ├── img │ │ │ ├── control.svg │ │ │ └── icon.png │ │ └── javascript │ │ │ └── Leaflet.StyleEditor.min.js │ ├── leaflet.draw │ │ ├── images │ │ │ ├── spritesheet-2x.png │ │ │ └── spritesheet.svg │ │ ├── leaflet.draw-src.css │ │ ├── leaflet.draw-src.js │ │ ├── leaflet.draw-src.map │ │ ├── leaflet.draw.css │ │ └── leaflet.draw.js │ ├── leaflet.fullscreen │ │ ├── Control.FullScreen.css │ │ ├── Control.FullScreen.js │ │ ├── icon-fullscreen-2x.png │ │ ├── icon-fullscreen.png │ │ └── package.json │ ├── leaflet.markercluster │ │ ├── MarkerCluster.css │ │ └── leaflet.markercluster.js │ └── leaflet │ │ ├── images │ │ ├── layers-2x.png │ │ ├── layers.png │ │ ├── marker-icon-2x.png │ │ ├── marker-icon.png │ │ └── marker-shadow.png │ │ ├── leaflet.css │ │ └── leaflet.js └── semanticMaps.js ├── src ├── DataAccess │ ├── CachingGeocoder.php │ ├── GeoJsonFetcher.php │ ├── GeoJsonFetcherResult.php │ ├── Image.php │ ├── ImageRepository.php │ ├── MapsFileFetcher.php │ ├── MediaWikiFileUrlFinder.php │ ├── MwImage.php │ ├── MwImageRepository.php │ └── PageContentFetcher.php ├── FileUrlFinder.php ├── GeoFunctions.php ├── GeoJsonPages │ ├── GeoJsonContent.php │ ├── GeoJsonContentHandler.php │ ├── GeoJsonLegacyContent.php │ ├── GeoJsonMapPageUi.php │ ├── GeoJsonNewPageUi.php │ ├── GeoJsonStore.php │ └── Semantic │ │ ├── SemanticGeoJsonStore.php │ │ ├── SubObject.php │ │ └── SubObjectBuilder.php ├── GoogleMapsService.php ├── LeafletService.php ├── LegacyMapEditor │ ├── MapEditorHtml.php │ └── SpecialMapEditor.php ├── LegacyModel │ ├── BaseElement.php │ ├── BaseFillableElement.php │ ├── BaseStrokableElement.php │ ├── Circle.php │ ├── ImageOverlay.php │ ├── Line.php │ ├── Location.php │ ├── Polygon.php │ ├── Rectangle.php │ └── WmsOverlay.php ├── Map │ ├── CargoFormat │ │ ├── CargoFormat.php │ │ └── CargoOutputBuilder.php │ ├── DisplayMap │ │ ├── DisplayMapFunction.php │ │ └── DisplayMapRenderer.php │ ├── MapData.php │ ├── MapDataSerializer.php │ ├── MapHtmlBuilder.php │ ├── MapOutput.php │ ├── MapOutputBuilder.php │ ├── Marker.php │ ├── SemanticFormat │ │ └── MapPrinter.php │ └── StructuredPopup.php ├── MappingService.php ├── MappingServices.php ├── MapsFactory.php ├── MapsFunctions.php ├── MapsHooks.php ├── MapsRegistration.php ├── MapsSetup.php ├── ParserHookSetup.php ├── ParserHooks │ ├── CoordinatesFunction.php │ ├── DistanceFunction.php │ ├── FindDestinationFunction.php │ ├── GeoDistanceFunction.php │ ├── GeocodeFunction.php │ └── MapsDocFunction.php ├── Presentation │ ├── CoordinateFormatter.php │ ├── ElementJsonSerializer.php │ ├── KmlFormatter.php │ ├── MapsDistanceParser.php │ ├── OutputFacade.php │ ├── ParameterExtractor.php │ └── WikitextParser.php ├── SemanticMW │ ├── AreaDescription.php │ ├── CoordinateDescription.php │ ├── CoordinateValue.php │ ├── KmlPrinter.php │ ├── QueryHandler.php │ └── TemplatedPopup.php ├── SemanticMapsSetup.php └── WikitextParsers │ ├── CircleParser.php │ ├── DistanceParser.php │ ├── ImageOverlayParser.php │ ├── LineParser.php │ ├── LocationParser.php │ ├── PolygonParser.php │ ├── RectangleParser.php │ └── WmsOverlayParser.php └── tests ├── Integration ├── DataAccess │ ├── GeoJsonFetcherTest.php │ └── MediaWikiFileUrlFinderTest.php ├── GeoJsonPages │ └── Semantic │ │ └── SubObjectBuilderTest.php ├── InitializationTest.php ├── MapsDistanceParserTest.php ├── MapsMapperTest.php ├── MapsSetupTest.php ├── Parser │ ├── CoordinatesTest.php │ ├── DisplayMapTest.php │ ├── DistanceTest.php │ ├── FindDestinationTest.php │ ├── GeoDistanceTest.php │ ├── GoogleMapsTest.php │ ├── LeafletTest.php │ └── MapsDocTest.php ├── Parsers │ ├── CircleParserTest.php │ ├── DistanceParserTest.php │ ├── LineParserTest.php │ ├── LocationParserTest.php │ ├── RectangleParserTest.php │ └── WmsOverlayParserTest.php ├── ResourceModulesTest.php └── Semantic │ └── AreaDescriptionTest.php ├── MapsTestFactory.php ├── System └── SemanticQueryTest.php ├── TestDoubles ├── ImageValueObject.php └── InMemoryImageRepository.php ├── Unit ├── DataAccess │ └── SubObjectTest.php ├── GeoJsonPages │ └── GeoJsonContentTest.php ├── LegacyModel │ ├── BaseElementTest.php │ ├── CircleTest.php │ ├── ImageOverlayTest.php │ ├── LineTest.php │ ├── LocationTest.php │ ├── PolygonTest.php │ └── RectangleTest.php ├── Map │ └── StructuredPopupTest.php ├── Presentation │ ├── KmlFormatterTest.php │ └── ParameterExtractorTest.php └── SemanticMW │ ├── CoordinateValueTest.php │ └── TemplatedPopupTest.php ├── Util ├── PageCreator.php └── TestFactory.php ├── bootstrap.php └── js └── leaflet └── GeoJsonTest.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: JeroenDeDauw 4 | patreon: 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Setup 11 | 12 | - Maps version: 13 | - MW version: 14 | - PHP version: 15 | - SMW version (if applicable): 16 | 17 | ### Issue 18 | 19 | Detailed description of the issue and a [stack trace](https://www.semantic-mediawiki.org/wiki/Help:Identifying_bugs) if applicable: 20 | 21 | ``` 22 | ``` 23 | 24 | Steps to reproduce the observation (recommendation is to use the [sandbox](https://sandbox.semantic-mediawiki.org)): 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Request a new feature 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 2 | 3 | version: 2 4 | updates: 5 | - package-ecosystem: "composer" 6 | directory: "/" # Location of package manifests 7 | schedule: 8 | interval: "daily" 9 | -------------------------------------------------------------------------------- /.github/workflows/installMediaWiki.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -ex 4 | 5 | MW_BRANCH=$1 6 | EXTENSION_NAME=$2 7 | 8 | wget "https://github.com/wikimedia/mediawiki/archive/refs/heads/$MW_BRANCH.tar.gz" -nv 9 | 10 | tar -zxf $MW_BRANCH.tar.gz 11 | mv mediawiki-$MW_BRANCH mediawiki 12 | 13 | cd mediawiki 14 | 15 | composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader 16 | 17 | php maintenance/install.php \ 18 | --dbtype sqlite \ 19 | --dbuser root \ 20 | --dbname mw \ 21 | --dbpath "$(pwd)" \ 22 | --scriptpath="" \ 23 | --pass AdminPassword WikiName AdminUser 24 | 25 | echo 'error_reporting(E_ALL| E_STRICT);' >> LocalSettings.php 26 | echo 'ini_set("display_errors", 1);' >> LocalSettings.php 27 | echo '$wgShowExceptionDetails = true;' >> LocalSettings.php 28 | echo '$wgShowDBErrorBacktrace = true;' >> LocalSettings.php 29 | echo '$wgDevelopmentWarnings = true;' >> LocalSettings.php 30 | 31 | echo 'wfLoadExtension( "'$EXTENSION_NAME'" );' >> LocalSettings.php 32 | 33 | cat <> composer.local.json 34 | { 35 | "require": {}, 36 | "extra": { 37 | "merge-plugin": { 38 | "merge-dev": true, 39 | "include": [ 40 | "extensions/$EXTENSION_NAME/composer.json" 41 | ] 42 | } 43 | } 44 | } 45 | EOT 46 | -------------------------------------------------------------------------------- /.github/youtube.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/.github/youtube.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.kate-swp 3 | 4 | !.* 5 | .idea/ 6 | 7 | vendor/ 8 | extensions/ 9 | 10 | composer.phar 11 | composer.lock 12 | 13 | phpunit.phar 14 | .phpunit.result.cache 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: ci test cs phpunit phpcs stan stan-baseline psalm psalm-baseline baseline 2 | 3 | ci: test cs 4 | test: phpunit stan 5 | cs: phpcs 6 | 7 | phpunit: 8 | php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist 9 | 10 | phpcs: 11 | cd ../.. && vendor/bin/phpcs -p -s --standard=$(shell pwd)/phpcs.xml 12 | 13 | stan: 14 | ../../vendor/bin/phpstan analyse --configuration=phpstan.neon --memory-limit=2G 15 | 16 | stan-baseline: 17 | ../../vendor/bin/phpstan analyse --configuration=phpstan.neon --memory-limit=2G --generate-baseline 18 | 19 | psalm: 20 | ../../vendor/bin/psalm --config=psalm.xml 21 | 22 | psalm-baseline: 23 | ../../vendor/bin/psalm --set-baseline=psalm-baseline.xml 24 | 25 | baseline: stan-baseline psalm-baseline 26 | -------------------------------------------------------------------------------- /Maps.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | 14 | wfLoadExtension( 'Maps' ); -------------------------------------------------------------------------------- /Maps_Settings.php: -------------------------------------------------------------------------------- 1 | $value ) { 13 | $GLOBALS[$key] = $value; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mediawiki/maps", 3 | "type": "mediawiki-extension", 4 | "description": "Adds various mapping features to MediaWiki", 5 | "keywords": [ 6 | "MediaWiki", 7 | "Semantic MediaWiki", 8 | "Maps", 9 | "Semantic Maps", 10 | "Google Maps", 11 | "OpenLayers", 12 | "OSM", 13 | "Leaflet", 14 | "Geocode", 15 | "Geocoding", 16 | "OpenStreetMap" 17 | ], 18 | "homepage": "https://maps.extension.wiki", 19 | "license": "GPL-2.0-or-later", 20 | "authors": [ 21 | { 22 | "name": "Jeroen De Dauw", 23 | "email": "jeroendedauw@gmail.com", 24 | "homepage": "https://EntropyWins.wtf", 25 | "role": "Creator and lead developer" 26 | }, 27 | { 28 | "name": "Professional Wiki", 29 | "email": "info@professional.wiki", 30 | "homepage": "https://professional.wiki" 31 | } 32 | ], 33 | "support": { 34 | "email": "semediawiki-user@lists.sourceforge.net", 35 | "irc": "irc://irc.libera.chat/mediawiki", 36 | "source": "https://github.com/ProfessionalWiki/Maps" 37 | }, 38 | "require": { 39 | "php": ">=7.4", 40 | "composer/installers": "^2.0.0|^1.0.1", 41 | "mediawiki/parser-hooks": "~1.5", 42 | "param-processor/param-processor": "^1.10", 43 | "data-values/geo": "~4.0|~3.0", 44 | "jeroen/file-fetcher": "~6.0|~5.0", 45 | "jeroen/file-fetcher-cache": "~1.0", 46 | "jeroen/simple-cache": "~2.0", 47 | "jeroen/simple-geocoder": "~1.2", 48 | "jmikola/geojson": "^1.0.2" 49 | }, 50 | "require-dev": { 51 | "vimeo/psalm": "^5.0.0", 52 | "phpstan/phpstan": "^1.4.8", 53 | "mediawiki/mediawiki-codesniffer": "39.0.0" 54 | }, 55 | "autoload": { 56 | "psr-4": { 57 | "Maps\\": "src/", 58 | "Maps\\Tests\\": "tests/" 59 | }, 60 | "files": [ 61 | "Maps_Settings.php" 62 | ] 63 | }, 64 | "replace": { 65 | "mediawiki/semantic-maps": "*" 66 | }, 67 | "suggest": { 68 | "mediawiki/semantic-media-wiki": "Add, edit, aggregate and visualize structured coordinate data stored with Semantic MediaWiki" 69 | }, 70 | "config": { 71 | "process-timeout": 0, 72 | "allow-plugins": { 73 | "composer/installers": true 74 | } 75 | }, 76 | "scripts":{ 77 | "ci": "php ../../tests/phpunit/phpunit.php -c phpunit.xml.dist" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /i18n/af.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Naudefj", 5 | "Rooiratel" 6 | ] 7 | }, 8 | "maps_map": "Kaart", 9 | "maps-fullscreen-button": "Wissel volskerm", 10 | "maps-abb-north": "N", 11 | "maps-abb-east": "O", 12 | "maps-abb-south": "S", 13 | "maps-abb-west": "W", 14 | "maps-latitude": "Breedte:", 15 | "maps-longitude": "Lengte:", 16 | "maps_coordinates_missing": "Geen koördinate is vir die kaart verskaf nie.", 17 | "maps_unrecognized_coords": "Die volgende koördinate is nie herken nie: $1.", 18 | "maps_unrecognized_coords_for": "Die volgende {{PLURAL:$2|koördinaat|koördinate}} is nie herken nie en is uit die kaart weggelaat:\n$1.", 19 | "maps_map_cannot_be_displayed": "Die kaart kan nie vertoon word nie.", 20 | "maps_click_to_activate": "Kliek om die kaart te aktiveer", 21 | "maps_centred_on": "Kaart gesentreer op $1, $2.", 22 | "mapeditor-none-text": "Geen", 23 | "mapeditor-link-title-switcher-link-text": "Skakel", 24 | "mapeditor-form-field-title": "Titel", 25 | "mapeditor-form-field-text": "Teks", 26 | "mapeditor-form-field-icon": "Ikoon", 27 | "mapeditor-form-field-group": "Groep", 28 | "semanticmaps-unrecognizeddistance": "Die waarde \"$1\" is nie 'n geldige afstand nie.", 29 | "maps-validator-type-string": "teks", 30 | "maps-validator-type-integer": "geheel getal", 31 | "maps-validator-type-float": "getal", 32 | "maps-validator-type-boolean": "ja/nee", 33 | "maps-validator-type-char": "karakter", 34 | "maps-validator-type-string-list": "lys met tekste", 35 | "maps-validator-type-integer-list": "lys met gehele getalle", 36 | "maps-validator-type-float-list": "lys met getalle", 37 | "maps-validator-type-boolean-list": "lys met ja/nee", 38 | "maps-validator-type-char-list": "lys met karakters", 39 | "maps-validator-type-title-list": "lys met titels", 40 | "maps-validator-fatal-error": "Onherstelbare fout: $1", 41 | "maps-validator-describe-header-parameter": "Parameter", 42 | "maps-validator-describe-header-type": "Tipe", 43 | "maps-validator-describe-header-default": "Standaard", 44 | "maps-validator-describe-header-description": "Beskrywing", 45 | "maps-validator-describe-required": "verpligtend", 46 | "maps-validator-describe-empty": "leeg", 47 | "maps-validator-message-nodesc": "Geen beskrywing" 48 | } 49 | -------------------------------------------------------------------------------- /i18n/aln.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Mdupont" 5 | ] 6 | }, 7 | "maps-desc": "Ofron mundësinë për të shfaqur koordinimin e të dhënave në harta, dhe adresat geocode ([http://mapping.referata.com/wiki/Maps_examples demo]). Hartës shërbimet në dispozicion: $1", 8 | "maps_map": "Hartë", 9 | "maps-loading-map": "Loading Harta ...", 10 | "maps-abb-north": "N", 11 | "maps-abb-east": "E", 12 | "maps-abb-south": "S", 13 | "maps-abb-west": "W", 14 | "maps-latitude": "Latitude:", 15 | "maps-longitude": "Gjatësi:", 16 | "maps-invalid-coordinates": "Vlera $1 nuk është njohur si një grup të vlefshme të kordinatave.", 17 | "maps_coordinates_missing": "Nuk ka koordinon parashikuara në hartë.", 18 | "maps_geocoding_failed": "Më poshtë {{PLURAL:$2|Adresa|adresat}} nuk mund të geocoded: $1." 19 | } 20 | -------------------------------------------------------------------------------- /i18n/arc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Basharh" 5 | ] 6 | }, 7 | "maps_map": "ܨܘܪܬ ܥܠܡܐ", 8 | "maps-layer-property": "ܕܝܠܝܬܐ", 9 | "maps-layer-value": "ܛܝܡܐ", 10 | "maps-layer-errors": "ܦܘܕ̈ܐ", 11 | "maps-abb-north": "ܓܪܒܝܐ", 12 | "maps-abb-east": "ܡܕܢܚܐ", 13 | "maps-abb-south": "ܬܝܡܢܐ", 14 | "maps-abb-west": "ܡܥܪܒܐ" 15 | } 16 | -------------------------------------------------------------------------------- /i18n/arz.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Meno25" 5 | ] 6 | }, 7 | "maps-abb-north": "شمال", 8 | "maps-abb-east": "شرق", 9 | "maps-abb-south": "جنوب", 10 | "maps-abb-west": "غرب", 11 | "maps-latitude": "دوائر العرض:", 12 | "maps-longitude": "خطوط الطول:" 13 | } 14 | -------------------------------------------------------------------------------- /i18n/az.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "AZISS", 5 | "Cekli829", 6 | "Nemoralis", 7 | "Əkrəm", 8 | "Əkrəm Cəfər" 9 | ] 10 | }, 11 | "maps_map": "Xəritə", 12 | "maps-loading-map": "Xəritə yüklənir...", 13 | "maps-layer-errors": "Xətalar", 14 | "maps-mylocation-button-tooltip": "Xəritədə mənim yerimi göstər.", 15 | "maps-abb-north": "Şm", 16 | "maps-abb-east": "C", 17 | "maps-abb-west": "Q", 18 | "maps-par-enable-mylocation": "Geolokasiya düyməsini aktivləşdir", 19 | "maps-par-enable-mylocationfollow": "Davamlı olaraq xəritəni istifadəçinin lokasiyasında mərkəzlə", 20 | "semanticmaps-unrecognizeddistance": "$1 rəqəmi mümkün olmayan məsafədir.", 21 | "semanticmaps-kml-link": "KML faylına bax", 22 | "semanticmaps-default-kml-pagelink": "$1 səhifəyə bax", 23 | "semanticmaps-latitude": "En dairəsi: $1", 24 | "semanticmaps-longitude": "Uzunluq dairəsi: $1", 25 | "semanticmaps-altitude": "Hündürlük: $1" 26 | } 27 | -------------------------------------------------------------------------------- /i18n/ba.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Haqmar", 5 | "Вильданова Гюзель", 6 | "З. ӘЙЛЕ", 7 | "Ләйсән" 8 | ] 9 | }, 10 | "maps-leaflet-par-defzoom": "Программа көйләүе буйынса карта масштабын билдәләү мөмкинлеген бирә.", 11 | "mapeditor-form-title": "Мәғлүмәттәрҙе үҙгәртеү", 12 | "mapeditor-link-title-switcher-link-text": "Һылтанма", 13 | "mapeditor-form-field-title": "Атама", 14 | "mapeditor-form-field-text": "Текст", 15 | "mapeditor-form-field-link": "Һылтанма", 16 | "mapeditor-form-field-group": "Төркөм" 17 | } 18 | -------------------------------------------------------------------------------- /i18n/bar.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Mucalexx" 5 | ] 6 | }, 7 | "maps-loading-map": "Korten werd gloon ..." 8 | } 9 | -------------------------------------------------------------------------------- /i18n/bg.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "DCLXVI", 5 | "Plamen", 6 | "StanProg", 7 | "Ted Masters", 8 | "පසිඳු කාවින්ද" 9 | ] 10 | }, 11 | "maps_map": "Карта", 12 | "maps-copycoords-prompt": "CTRL+C, ENTER", 13 | "maps-ns-layer": "Слой", 14 | "maps-layer-value": "Стойност", 15 | "maps-layer-errors": "Грешки", 16 | "maps-fullscreen-button": "Превключване на режима на цял екран", 17 | "maps-abb-north": "С", 18 | "maps-abb-east": "И", 19 | "maps-abb-south": "Ю", 20 | "maps-abb-west": "З", 21 | "maps-latitude": "Географска ширина:", 22 | "maps-longitude": "Географска дължина:", 23 | "maps-googlemaps3-incompatbrowser": "Вашият браузър не е съвместим с Google Maps v3.", 24 | "mapeditor": "Редактор на карти", 25 | "mapeditor-done-button": "Готово", 26 | "mapeditor-remove-button": "Премахване", 27 | "mapeditor-import-button2": "Внасяне", 28 | "mapeditor-code-title": "Уики код", 29 | "mapeditor-link-title-switcher-link-text": "Препратка", 30 | "mapeditor-form-field-title": "Заглавие", 31 | "mapeditor-form-field-text": "Текст", 32 | "mapeditor-form-field-link": "Препратка", 33 | "mapeditor-form-field-group": "Група", 34 | "mapeditor-form-field-image": "Изображение", 35 | "maps-json-editor-toolbar-save-text": "Готово", 36 | "maps-json-editor-toolbar-cancel-text": "Отказ", 37 | "maps-validator-type-string": "текст", 38 | "maps-validator-type-float": "число", 39 | "maps-validator-type-boolean": "да/не", 40 | "maps-validator-describe-header-type": "Псевдоними", 41 | "maps-validator-describe-header-default": "По подразбиране", 42 | "maps-validator-describe-header-description": "Описание", 43 | "maps-validator-describe-empty": "празно", 44 | "maps-validator-message-nodesc": "Не е въведено описание" 45 | } 46 | -------------------------------------------------------------------------------- /i18n/bjn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Ezagren" 5 | ] 6 | }, 7 | "maps-abb-north": "U", 8 | "maps-abb-east": "T", 9 | "maps-abb-south": "S", 10 | "maps-abb-west": "B" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/cu.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "ОйЛ" 5 | ] 6 | }, 7 | "maps-layer-errors": "блаꙁнꙑ" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/cv.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Chuvash2014" 5 | ] 6 | }, 7 | "mapeditor-form-field-image": "Ӳкерчĕк" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/diq.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Erdemaslancan", 5 | "Gorizon", 6 | "Kumkumuk", 7 | "Mirzali" 8 | ] 9 | }, 10 | "maps_map": "Xerita", 11 | "maps-copycoords-prompt": "CTRL+C, ENTER", 12 | "maps-others": "ê bini", 13 | "maps-layer-value": "Erc", 14 | "maps-abb-north": "Z", 15 | "maps-abb-east": "A", 16 | "maps-abb-south": "V", 17 | "maps-abb-west": "P", 18 | "maps-latitude": "Verıniye:", 19 | "maps-longitude": "Derganiye:", 20 | "maps_coordinates_missing": "Qan dê xerita koordinat nêdebya.", 21 | "maps_googlemaps3": "Google Maps v3", 22 | "mapeditor": "Vurnerê Xerita", 23 | "mapeditor-none-text": "Çıniyo", 24 | "mapeditor-done-button": "Qeyd ke", 25 | "mapeditor-remove-button": "Hewad", 26 | "mapeditor-import-button2": "Zerre ke", 27 | "mapeditor-code-title": "Wiki kode", 28 | "mapeditor-form-title": "Teferuatan bıvurne", 29 | "mapeditor-link-title-switcher-link-text": "Gıre", 30 | "mapeditor-form-field-title": "Sername", 31 | "mapeditor-form-field-text": "Metın", 32 | "mapeditor-form-field-link": "Gıre", 33 | "mapeditor-form-field-icon": "Ikon", 34 | "mapeditor-form-field-group": "Grube", 35 | "mapeditor-form-field-image": "Resım", 36 | "semanticmaps-forminput-locations": "Lokasyoni", 37 | "maps-json-editor-button-line": "Yew xete bance", 38 | "maps-json-editor-added-line": "Xeta cıbiyayiye", 39 | "maps-json-editor-toolbar-save-title": "Vurnayışan qeyd ke", 40 | "maps-json-editor-toolbar-save-text": "Qeyd ke", 41 | "maps-json-editor-toolbar-cancel-text": "Bıtexelne", 42 | "maps-json-editor-toolbar-clear-title": "Pelan pêro pak kerê", 43 | "maps-json-editor-toolbar-clear-text": "Pêriyûne bestere", 44 | "maps-json-editor-toolbar-button-edit": "Qatan bıvurne", 45 | "maps-json-editor-toolbar-button-remove": "Pelan bestere", 46 | "maps-geo-json-edit-source": "Çımeyi bıvurnê", 47 | "maps-geo-json-create-page-button": "Na pele vıraze", 48 | "maps-geo-json-create-page-creating": "Pele vıraziyena...", 49 | "maps-validator-type-float": "numre", 50 | "maps-validator-type-title": "sername", 51 | "maps-validator-describe-header-parameter": "Parametre", 52 | "maps-validator-describe-header-type": "Babet", 53 | "maps-validator-describe-header-default": "Hesabiyaye", 54 | "maps-validator-describe-header-description": "Şınasnayış", 55 | "maps-validator-describe-required": "lazım" 56 | } 57 | -------------------------------------------------------------------------------- /i18n/en-gb.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Bruce89", 5 | "Lloffiwr", 6 | "Reedy", 7 | "Shirayuki" 8 | ] 9 | }, 10 | "maps-displaymap-par-centre": "Allows setting the coordinates of the map's centre for display_point(s).\nAccepts both addresses and coordinates.\nWhen this property is not provided, the map will centre itself on the provided marker, or between the provided markers.", 11 | "maps-invalid-coordinates": "The value $1 was not recognised as a valid set of coordinates.", 12 | "maps_unrecognized_coords": "The following {{PLURAL:$2|coordinate was|coordinates were}} not recognised: $1.", 13 | "maps_unrecognized_coords_for": "The following {{PLURAL:$2|coordinate was|coordinates were}} not recognised and {{PLURAL:$2|has|have}} been omitted from the map:\n$1", 14 | "maps_centred_on": "Map centred on $1, $2.", 15 | "maps-par-centre": "The location on which the map should be centred", 16 | "mapeditor-form-field-strokecolor": "Stroke colour", 17 | "mapeditor-form-field-fillcolor": "Fill colour", 18 | "semanticmaps-par-centre": "The centre of the map. When not provided, the map will automatically pick the optimal centre to display all markers on the map." 19 | } 20 | -------------------------------------------------------------------------------- /i18n/et.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Avjoska", 5 | "Hendrik", 6 | "Pikne", 7 | "Rivoz" 8 | ] 9 | }, 10 | "maps_map": "Kaart", 11 | "maps-loading-map": "Kaardi laadimine...", 12 | "maps-others": "teised", 13 | "maps-geocode-par-location": "Geokodeeritav aadress.", 14 | "maps-displaymap-par-maxzoom": "Maksimaalne suumi tase", 15 | "maps-displaymap-par-minzoom": "Minimaalne suumi tase", 16 | "maps-abb-north": "N", 17 | "maps-abb-east": "E", 18 | "maps-abb-south": "S", 19 | "maps-abb-west": "W", 20 | "maps-latitude": "Laius:", 21 | "maps-longitude": "Pikkus:", 22 | "maps_map_cannot_be_displayed": "Kaarti ei saa kuvada.", 23 | "mapeditor": "Kaardi toimetaja", 24 | "mapeditor-none-text": "Puudub", 25 | "mapeditor-done-button": "Valmis", 26 | "mapeditor-remove-button": "Eemalda", 27 | "mapeditor-import-button2": "Impordi", 28 | "mapeditor-clear-button": "Tühjenda kaart", 29 | "mapeditor-code-title": "Viki kood", 30 | "mapeditor-form-title": "Muuda üksikasju", 31 | "mapeditor-link-title-switcher-popup-text": "Hüpikaken tekstiga", 32 | "mapeditor-form-field-title": "Pealkiri", 33 | "mapeditor-form-field-text": "Tekst", 34 | "mapeditor-form-field-link": "Link", 35 | "mapeditor-form-field-icon": "Ikoon", 36 | "mapeditor-form-field-group": "Rühm", 37 | "mapeditor-form-field-image": "Pilt", 38 | "semanticmaps-latitude": "Laius: $1", 39 | "semanticmaps-longitude": "Pikkus: $1", 40 | "semanticmaps-altitude": "Kõrgus: $1", 41 | "maps-validator-type-string": "tekst", 42 | "maps-validator-type-integer": "täisarv", 43 | "maps-validator-type-float": "number", 44 | "maps-validator-type-boolean": "jah/ei", 45 | "maps-validator-type-char": "märk", 46 | "maps-validator-type-title": "pealkiri", 47 | "maps-validator-type-string-list": "tekstide loetelu", 48 | "maps-validator-type-float-list": "numbrite loetelu", 49 | "maps-validator-describe-header-parameter": "Parameeter", 50 | "maps-validator-describe-header-type": "Tüüp", 51 | "maps-validator-describe-header-description": "Kirjeldus", 52 | "maps-validator-describe-required": "nõutav", 53 | "maps-validator-describe-empty": "tühi" 54 | } 55 | -------------------------------------------------------------------------------- /i18n/fur.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Klenje" 5 | ] 6 | }, 7 | "maps-desc": "Al furnìs la possibilitât di mostrâ i dâts de coordinadis e lis direzions geocodificadis intune mape ([http://mapping.referata.com/wiki/Maps_examples demo]).\nServizis di mapis disponibii: $1", 8 | "maps_map": "Mape", 9 | "maps_coordinates_missing": "Nissune coordenade furnide pe mape.", 10 | "maps_geocoding_failed": "{{PLURAL:$2|La direzion ca sot no pues jessi geocodificade|Lis direzions ca sot no puedin jessi geocodificadis}}: $1.\nLa mape no pues jessi mostrade.", 11 | "maps_geocoding_failed_for": "{{PLURAL:$2|La direzion|Lis direzions}} ca sot no {{PLURAL:$2|pues|puedin}} jessi {{PLURAL:$2|geocodificade|geocodificadis}} e {{PLURAL:$2|no je mostrade|no son mostradis}} te mape:\n$1" 12 | } 13 | -------------------------------------------------------------------------------- /i18n/fy.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Robin van der Vliet", 5 | "Robin0van0der0vliet" 6 | ] 7 | }, 8 | "maps-layer-property": "Eigenskip", 9 | "maps-layer-value": "Wearde", 10 | "mapeditor-form-field-image": "Ôfbylding", 11 | "semanticmaps-latitude": "Breedtegraad: $1", 12 | "semanticmaps-longitude": "Lingtegraad: $1", 13 | "semanticmaps-forminput-locations": "Lokaasjes" 14 | } 15 | -------------------------------------------------------------------------------- /i18n/ga.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "පසිඳු කාවින්ද" 5 | ] 6 | }, 7 | "mapeditor-none-text": "Tada", 8 | "maps-validator-describe-header-type": "Cineál", 9 | "maps-validator-describe-header-default": "réamhshocrú" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/grc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [] 4 | }, 5 | "maps-abb-north": "Β", 6 | "maps-abb-east": "Α", 7 | "maps-abb-south": "Ν", 8 | "maps-abb-west": "Δ", 9 | "maps-latitude": "Πλάτος γεωγραφικόν:", 10 | "maps-longitude": "Μῆκος γεωγραφικόν:" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/ht.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [] 4 | }, 5 | "maps-abb-north": "N", 6 | "maps-abb-east": "E", 7 | "maps-abb-south": "S", 8 | "maps-abb-west": "W" 9 | } 10 | -------------------------------------------------------------------------------- /i18n/ig.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Edith Nwobu", 5 | "Ukabia" 6 | ] 7 | }, 8 | "maps_map": "Akààlà", 9 | "maps-displaymap-par-copycoords": "Gosi mkparịta ụka oge ị na-apị ngosi ebe e nwere ike isi bite nhazi ya", 10 | "mapeditor-link-title-switcher-link-text": "Òjikọ", 11 | "mapeditor-form-field-link": "Òjikọ" 12 | } 13 | -------------------------------------------------------------------------------- /i18n/jv.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [] 4 | }, 5 | "maps-abb-north": "L", 6 | "maps-abb-east": "W", 7 | "maps-abb-south": "Kdl", 8 | "maps-abb-west": "Kln", 9 | "maps-latitude": "Latituda:", 10 | "maps-longitude": "Longituda:" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/km.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "គីមស៊្រុន" 5 | ] 6 | }, 7 | "maps_map": "ផែនទី", 8 | "maps-loading-map": "កំពុងផ្ទុកផែនទី...", 9 | "maps-load-failed": "ផ្ទុកផែនទីមិនបានសម្រេចទេ!", 10 | "maps-markers": "សញ្ញាសម្គាល់", 11 | "maps-others": "ផ្សេងទៀត", 12 | "maps-abb-north": "ជ", 13 | "maps-abb-east": "ក", 14 | "maps-abb-south": "ត", 15 | "maps-abb-west": "ល", 16 | "maps-latitude": "រយៈទទឹង៖", 17 | "maps-longitude": "រយៈបណ្តោយ៖", 18 | "mapeditor-form-field-group": "ក្រុម" 19 | } 20 | -------------------------------------------------------------------------------- /i18n/ku-latn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Cûndûllah el-Kurdî", 5 | "George Animal", 6 | "Ghybu", 7 | "Gomada" 8 | ] 9 | }, 10 | "maps_map": "Nexşe", 11 | "maps-loading-map": "Nexşe tê barkirin...", 12 | "maps-copycoords-prompt": "CTRL+C,ENTER", 13 | "maps-others": "ên din", 14 | "maps-abb-north": "Bakur", 15 | "maps-abb-east": "Rojhilat", 16 | "maps-abb-south": "Başûr", 17 | "maps-abb-west": "Rojava", 18 | "mapeditor-link-title-switcher-link-text": "Girêdan", 19 | "mapeditor-form-field-title": "Sernav", 20 | "mapeditor-form-field-link": "Girêdan", 21 | "mapeditor-form-field-group": "Kom", 22 | "mapeditor-form-field-image": "Wêne", 23 | "semanticmaps-latitude": "Hêlîpan: $1", 24 | "semanticmaps-longitude": "Hêlîlar: $1", 25 | "semanticmaps-forminput-locations": "Cih", 26 | "semanticmaps-kml-title": "Sernavê standart ê ji bo encaman", 27 | "maps-validator-type-string": "nivîs", 28 | "maps-validator-type-boolean": "erê/na", 29 | "maps-validator-describe-header-parameter": "Parametre", 30 | "maps-validator-describe-header-type": "Cure", 31 | "maps-validator-describe-header-description": "Danasîn", 32 | "maps-validator-describe-empty": "vala" 33 | } 34 | -------------------------------------------------------------------------------- /i18n/lad.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Universal Life" 5 | ] 6 | }, 7 | "maps_map": "Mapa", 8 | "maps-loading-map": "Cargando la mapa...", 9 | "maps-abb-north": "N", 10 | "maps-abb-east": "E", 11 | "maps-abb-south": "S", 12 | "maps-abb-west": "O" 13 | } 14 | -------------------------------------------------------------------------------- /i18n/lv.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "GreenZeb", 5 | "Papuass" 6 | ] 7 | }, 8 | "maps-abb-north": "Z", 9 | "maps-abb-east": "A", 10 | "maps-abb-south": "D", 11 | "maps-abb-west": "R", 12 | "maps-latitude": "Platums:", 13 | "maps-longitude": "Garums:", 14 | "maps-json-editor-button-polygon": "Zīmēt daudzstūri", 15 | "maps-json-editor-edit-failed": "Neizdevās saglabāt karti.", 16 | "maps-json-editor-toolbar-save-title": "Iziet no labošanas režīma, saglabājot visas izmaiņas", 17 | "maps-json-editor-toolbar-save-text": "Pabeigts", 18 | "maps-json-editor-toolbar-cancel-text": "Atcelt", 19 | "maps-json-editor-toolbar-clear-text": "Dzēst visu", 20 | "maps-json-editor-toolbar-button-edit": "Labot slāņus", 21 | "maps-json-editor-toolbar-button-remove": "Dzēst slāņus", 22 | "maps-json-editor-toolbar-button-save": "Saglabāt izmaiņas", 23 | "maps-json-editor-changes-saved": "Tavas veiktās izmaiņas ir saglabātas", 24 | "maps-geo-json-create-source": "Izveidot no GeoJSON avota", 25 | "maps-geo-json-create-page-button": "Izveidot šo lapu", 26 | "maps-geo-json-create-page-creating": "Izveido lapu...", 27 | "maps-editor-edit-geojson": "Labot GeoJSON slāni" 28 | } 29 | -------------------------------------------------------------------------------- /i18n/map-bms.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "StefanusRA" 5 | ] 6 | }, 7 | "maps-abb-north": "L", 8 | "maps-abb-east": "W", 9 | "maps-abb-south": "Kdl", 10 | "maps-abb-west": "Kln", 11 | "maps-latitude": "Latituda:", 12 | "maps-longitude": "Longituda:" 13 | } 14 | -------------------------------------------------------------------------------- /i18n/mg.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Jagwar" 5 | ] 6 | }, 7 | "maps-layerdef-invalid-fatal": "Famaritana ny tsy azo ampiasaina mahajanona", 8 | "maps-layerdef-wrong-namespace": "Azo ekena ao amin'ny eran'anarana \"$1\" ihany ny famaritan-tsosona", 9 | "maps-layerdef-equal-layer-name": "Tsy maintsy tokana ao amin'ny pejy sosona ny anaran-tsosona. Efa ampiasaina anaty sosona hafa \"$1\"", 10 | "maps-layerpage-usage": "Pejy misy sarintany mampiasa ny sosona \"$1\"", 11 | "maps-layerpage-nousage": "Tsy misy pejy mampiasa ity sosona ity amin'izao fotoana izao.", 12 | "validation-error-invalid-layer": "Tsy maintsy sosona azo ampiasaina ny parametatra \"$1\"", 13 | "validation-error-invalid-layers": "Tsy maintsy parametatra azo ekena iray na mihoatra ny parametatra \"$1\".", 14 | "validation-error-no-non-numerics": "Tsy maintsy tohin-dohavy iray na maro ny parametatra \"$1\".", 15 | "maps-layer-of-type": "Karazan-tsosona \"$1\"", 16 | "maps-layerdefinition-description": "Mamaritra sosona manokana izay mety aseho amin'ny alalan'ny lefan-tsaritany hafa.", 17 | "validation-error-invalid-location": "Tsy maintsy toerana azo ekena ny parametatra \"$1\".", 18 | "validation-error-invalid-width": "Tsy maintsy habe azo ekena ny parametatra \"$1\".", 19 | "validation-error-invalid-height": "Tsy maintsy haavo azo ekena ny parametatra \"$1\".", 20 | "validation-error-invalid-distances": "Tsy maintsy halavirana azo ekena ny parametatra \"$1\".", 21 | "validation-error-invalid-image": "Tsy maintsy sary azo ekena ny parametatra \"$1\".", 22 | "validation-error-invalid-goverlay": "Tsy maintsy azo ampiasaina ny mpanosona \"$1\".", 23 | "validation-error-invalid-goverlays": "Tsy maintsy mpanosona azo ekena iray na mihoatra ny parametatra \"$1\".", 24 | "mapeditor-parser-error": "Nisy hadisoana nitranga teo am-panadihadiana ny solon'angona. Tsy norahararahiana ny iditry ny mpampiasa." 25 | } 26 | -------------------------------------------------------------------------------- /i18n/ml.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [] 4 | }, 5 | "maps-abb-north": "വടക്ക്", 6 | "maps-abb-east": "കിഴക്ക്", 7 | "maps-abb-south": "തെക്ക്", 8 | "maps-abb-west": "പടിഞ്ഞാറ്‌", 9 | "maps-latitude": "അക്ഷാംശം:", 10 | "maps-longitude": "രേഖാംശം:" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/mr.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "V.narsikar" 5 | ] 6 | }, 7 | "maps-abb-north": "N", 8 | "maps-abb-east": "E", 9 | "maps-abb-south": "S", 10 | "maps-abb-west": "W", 11 | "maps-latitude": "अक्षांश:", 12 | "maps-longitude": "रेखांश:", 13 | "semanticmaps-default-kml-pagelink": "पान $1 बघा", 14 | "semanticmaps-latitude": "अक्षांश:$1", 15 | "semanticmaps-longitude": "रेखांश:$1", 16 | "semanticmaps-altitude": "उन्नतन:$1", 17 | "semanticmaps-forminput-locations": "ठिकाण", 18 | "semanticmaps-par-ajaxcoordproperty": "कोऑर्डिनेट्स च्या गुणधर्माचे नाव जे अजॅक्स पृच्छा निर्माणास वापरल्या जाते.", 19 | "semanticmaps-par-ajaxquery": "एक दुसरी पृच्छा जी अतिरिक्त कोऑर्डिनेट्स पुरवावयाला अजॅक्स मार्फत पाठविली जाते.", 20 | "maps-validator-type-dimension": "संख्या व आकाराचे एकक" 21 | } 22 | -------------------------------------------------------------------------------- /i18n/mt.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Chrisportelli" 5 | ] 6 | }, 7 | "maps-copycoords-prompt": "CTRL+C, ENTER", 8 | "maps-others": "oħrajn", 9 | "maps-ns-layer": "Livell", 10 | "maps-ns-layer-talk": "Diskussjoni livell", 11 | "maps-layer-property": "Proprjetà", 12 | "maps-layer-value": "Valur", 13 | "maps-layer-errors": "Żbalji", 14 | "validation-error-invalid-layer": "Il-parametru $1 irid ikun livell validu.", 15 | "validation-error-invalid-layers": "Il-parametru $1 irid ikun wieħed jew iktar livelli validi.", 16 | "maps-layer-of-type": "Livell tat-tip $1", 17 | "maps-googlemaps3-incompatbrowser": "Il-browżer li qiegħed tuża' mhuwiex kompatibbli ma' Google Maps v3.", 18 | "maps-googlemaps3-par-controls": "Il-kontrolli li trid tpoġġi fuq il-mappa.", 19 | "maps-googlemaps3-par-poi": "Uri l-punti ta' interess.", 20 | "mapeditor": "Editur tal-mappa", 21 | "mapeditor-none-text": "L-ebda", 22 | "mapeditor-done-button": "Lest", 23 | "mapeditor-remove-button": "Neħħi", 24 | "mapeditor-import-button2": "Importa", 25 | "mapeditor-export-button": "Esporta għal kodiċi wiki", 26 | "mapeditor-import-button": "Importa minn kodiċi wiki", 27 | "mapeditor-select-button": "Agħżel dan il-poligonu", 28 | "mapeditor-mapparam-button": "Immodifika l-parametri tal-mappa", 29 | "mapeditor-clear-button": "Naddaf il-mappa", 30 | "mapeditor-code-title": "Kodiċi wiki", 31 | "mapeditor-import-title": "Importa l-kodiċi wiki", 32 | "mapeditor-form-title": "Immodifika d-dettalji", 33 | "mapeditor-link-title-switcher-link-text": "Ħolqa", 34 | "mapeditor-form-field-title": "Titlu", 35 | "mapeditor-form-field-text": "Test", 36 | "mapeditor-form-field-link": "Ħolqa", 37 | "mapeditor-form-field-icon": "Ikona", 38 | "mapeditor-form-field-group": "Grupp", 39 | "mapeditor-form-field-fillcolor": "Kulur ta' ġewwa", 40 | "mapeditor-form-field-fillopcaity": "Opaċità ta' ġewwa", 41 | "mapeditor-mapparam-defoption": "-Agħżel parametru-", 42 | "mapeditor-form-field-image": "Stampa", 43 | "semanticmaps-kml-link": "Ara l-fajl KML", 44 | "semanticmaps-latitude": "Latitudni: $1", 45 | "semanticmaps-longitude": "Lonġitudini: $1", 46 | "semanticmaps-altitude": "Altitudni: $1", 47 | "semanticmaps-forminput-locations": "Postijiet" 48 | } 49 | -------------------------------------------------------------------------------- /i18n/myv.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Rueter" 5 | ] 6 | }, 7 | "maps-abb-north": "Веньэльйонкс", 8 | "maps-abb-east": "Чилисемайонкс", 9 | "maps-abb-south": "Чиньэльйонкс", 10 | "maps-abb-west": "Чивалгомайонкс", 11 | "maps-latitude": "Келезэ:", 12 | "maps-longitude": "Кувалмозо:", 13 | "maps-validator-type-mapslocation": "Мода лангсо косо" 14 | } 15 | -------------------------------------------------------------------------------- /i18n/nah.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Languaeditor" 5 | ] 6 | }, 7 | "maps-abb-north": "M", 8 | "maps-abb-east": "T", 9 | "maps-abb-south": "H", 10 | "mapeditor-form-field-group": "Nechikolli" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/nds.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Joachim Mos" 5 | ] 6 | }, 7 | "maps_map": "Koort", 8 | "maps-layer-property": "Egenschop", 9 | "maps-layer-errors": "Fählers", 10 | "maps-abb-north": "N", 11 | "maps-abb-east": "O", 12 | "maps-abb-south": "S", 13 | "maps-abb-west": "W", 14 | "maps-latitude": "Bredengraad:", 15 | "maps-longitude": "Längengraad:", 16 | "mapeditor": "Koorteneditor", 17 | "mapeditor-none-text": "Keen", 18 | "mapeditor-done-button": "Färtig", 19 | "mapeditor-remove-button": "Wegmaken", 20 | "mapeditor-import-button2": "Importeren", 21 | "mapeditor-code-title": "Wikicode", 22 | "mapeditor-link-title-switcher-link-text": "Lenk", 23 | "mapeditor-form-field-title": "Titel", 24 | "mapeditor-form-field-text": "Text", 25 | "mapeditor-form-field-link": "Lenk", 26 | "mapeditor-form-field-icon": "Symbool", 27 | "mapeditor-form-field-group": "Köppel", 28 | "mapeditor-form-field-image": "Bild", 29 | "semanticmaps-latitude": "Bredengraad: $1", 30 | "semanticmaps-longitude": "Längengraad: $1" 31 | } 32 | -------------------------------------------------------------------------------- /i18n/nl-informal.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "McDutchie", 5 | "Siebrand" 6 | ] 7 | }, 8 | "maps-geocoder-not-available": "Geocoderen via Maps is niet beschikbaar. Het geocoderen van je locatie is niet mogelijk.", 9 | "maps-par-width": "Maakt het mogelijk om de breedte van de kaart in te stellen. Standaard worden pixels als eenheid gebruikt, maar je kunt expliciet een van deze eenheden opgeven: px, ex, em, %.", 10 | "maps-par-height": "Maakt het mogelijk om de hoogte van de kaart in te stellen. Standaard worden pixels als eenheid gebruikt, maar je kunt expliciet een van deze eenheden opgeven: px, ex, em, %.", 11 | "maps-googlemaps3-incompatbrowser": "Je browser kan niet werken met Google Maps v3." 12 | } 13 | -------------------------------------------------------------------------------- /i18n/nn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Harald Khan", 5 | "Njardarlogar" 6 | ] 7 | }, 8 | "maps_map": "Kart", 9 | "maps-abb-north": "N", 10 | "maps-abb-east": "A", 11 | "maps-abb-south": "S", 12 | "maps-abb-west": "V", 13 | "maps-latitude": "Breiddegrad:", 14 | "maps-longitude": "Lengdegrad:", 15 | "maps_coordinates_missing": "Ingen koordinatar vart oppgjevne for kartet.", 16 | "maps_unrecognized_coords": "Dei fylgjande koordinatane vart ikkje kjende att: $1.", 17 | "maps_map_cannot_be_displayed": "Kartet kan ikkje verta vist.", 18 | "maps_click_to_activate": "Trykk for å aktivera kartet", 19 | "maps_centred_on": "Kart sentrert på $1, $2.", 20 | "semanticmaps-kml-link": "Sjå KML-fila", 21 | "semanticmaps-default-kml-pagelink": "Sjå sida $1", 22 | "semanticmaps-forminput-locations": "Stader" 23 | } 24 | -------------------------------------------------------------------------------- /i18n/or.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Odisha1" 5 | ] 6 | }, 7 | "maps-others": "ବାକିସବୁ", 8 | "maps-ns-layer": "ସ୍ତର", 9 | "maps-layer-value": "ମୂଲ୍ୟ" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/pdc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Xqt" 5 | ] 6 | }, 7 | "maps-abb-north": "N", 8 | "maps-abb-east": "O", 9 | "maps-abb-south": "S", 10 | "maps-abb-west": "W" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/ps.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Af420", 5 | "Ahmed-Najib-Biabani-Ibrahimkhel", 6 | "Amjad Khan" 7 | ] 8 | }, 9 | "maps_map": "نخشه", 10 | "maps-others": "نور", 11 | "maps-layer-property": "ځانتيا", 12 | "maps-layer-value": "ارزښت", 13 | "maps-layer-errors": "تېروتنې", 14 | "maps-abb-north": "سهـ", 15 | "maps-abb-east": "خ", 16 | "maps-abb-south": "سو", 17 | "maps-abb-west": "ل", 18 | "mapeditor-none-text": "هېڅ", 19 | "mapeditor-done-button": "ترسره شو", 20 | "mapeditor-remove-button": "غورځول", 21 | "mapeditor-link-title-switcher-link-text": "تړنه", 22 | "mapeditor-form-field-title": "سرليک", 23 | "mapeditor-form-field-text": "متن", 24 | "mapeditor-form-field-link": "تړنه", 25 | "mapeditor-form-field-group": "ډله", 26 | "mapeditor-form-field-image": "انځور", 27 | "semanticmaps-forminput-locations": "استوګنځي", 28 | "maps-validator-type-string": "متن", 29 | "maps-validator-type-float": "شمېره", 30 | "maps-validator-type-boolean": "هو/نه", 31 | "maps-validator-type-char": "لوښه", 32 | "maps-validator-type-float-list": "د شمېرو لړليک", 33 | "maps-validator-type-boolean-list": "د هو/نه لړليک", 34 | "maps-validator-type-char-list": "د لوښو لړليک", 35 | "maps-validator-describe-header-default": "تلواليز", 36 | "maps-validator-describe-header-description": "څرگندونه", 37 | "maps-validator-describe-empty": "تش" 38 | } 39 | -------------------------------------------------------------------------------- /i18n/rue.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Gazeb" 5 | ] 6 | }, 7 | "maps_map": "Мапа", 8 | "maps-loading-map": "Награваня мапы. . .", 9 | "maps-abb-north": "С", 10 | "maps-abb-east": "В", 11 | "maps-abb-south": "Ю", 12 | "maps-abb-west": "З", 13 | "maps-validator-type-string": "текст" 14 | } 15 | -------------------------------------------------------------------------------- /i18n/sco.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "AmaryllisGardener", 5 | "CiphriusKane", 6 | "John Reid", 7 | "MJL" 8 | ] 9 | }, 10 | "maps-tracking-category": "Pages wi a cairt rendered bi the Maps extension", 11 | "maps-layerpage-usage": "Pages wi cairts uisin layer \"$1\"", 12 | "maps-displaymap-par-visitedicon": "The filename o aen eemage tae be uised fer maurker icons efter the oreeginal maurkers hae been clap't", 13 | "maps-json-editor-toolbar-save-text": "Duin", 14 | "maps-json-editor-toolbar-clear-text": "Delete Aw", 15 | "maps-json-editor-toolbar-button-save": "Save cheenges", 16 | "maps-json-editor-changes-saved": "Yer chynges hae buin hain'd", 17 | "maps-validator-describe-header-description": "Descreeption" 18 | } 19 | -------------------------------------------------------------------------------- /i18n/sk.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Helix84", 5 | "Luky001", 6 | "Macofe" 7 | ] 8 | }, 9 | "maps-desc": "Umožňuje vkladať dynamické mapy do wiki stránok použitím Máp Google alebo Leaflet. Má vizuálny editor, voliteľne je integrovateľný so Sémantickou MediaWiki, podporuje GeoJSON a pridáva geokódovacie schopnosti.", 10 | "maps_map": "Mapa", 11 | "maps-abb-north": "S", 12 | "maps-abb-east": "V", 13 | "maps-abb-south": "J", 14 | "maps-abb-west": "Z", 15 | "maps-latitude": "Zem. dĺžka:", 16 | "maps-longitude": "Zem. šírka:", 17 | "maps_coordinates_missing": "Neboli poskytnuté žiadne súradnice.", 18 | "maps_geocoding_failed": "Nebolo možné určiť súradnice {{PLURAL:$2|nasledovnej adresy|nasledovných adries}}: $1.", 19 | "maps_geocoding_failed_for": "Nebolo možné určiť súradnice {{PLURAL:$2|nasledovnej adresy|nasledovných adries}} a {{PLURAL:$2|bola vynechaná|boli vynechané}} z mapy: $1." 20 | } 21 | -------------------------------------------------------------------------------- /i18n/sq.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Ammartivari", 5 | "Vanished 456321456" 6 | ] 7 | }, 8 | "mapeditor": "Redaktor hartash" 9 | } 10 | -------------------------------------------------------------------------------- /i18n/sw.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Kipala", 5 | "Lloffiwr", 6 | "Yasen igra" 7 | ] 8 | }, 9 | "maps_map": "Ramani", 10 | "maps-googlemaps3-par-imageoverlays": "Inaruhusu kuongeza picha mahali panapoonyeshwa", 11 | "semanticmaps-kml-link": "Tazama faili la KML", 12 | "semanticmaps-default-kml-pagelink": "Tazama ukurasa $1", 13 | "semanticmaps-forminput-locations": "Mahali" 14 | } 15 | -------------------------------------------------------------------------------- /i18n/te.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Chaduvari", 5 | "Ravichandra", 6 | "Veeven" 7 | ] 8 | }, 9 | "maps_map": "పటం", 10 | "maps-others": "ఇతరాలు", 11 | "maps-layer-property": "లక్షణం", 12 | "maps-layer-value": "విలువ", 13 | "maps-layer-errors": "పొరపాట్లు", 14 | "maps-abb-north": "ఉ", 15 | "maps-abb-east": "తూ", 16 | "maps-abb-south": "ద", 17 | "maps-abb-west": "ప", 18 | "maps-latitude": "అక్షాంశం:", 19 | "maps-longitude": "రేఖాంశం:", 20 | "mapeditor-none-text": "ఏమీలేదు", 21 | "mapeditor-done-button": "పూర్తయ్యింది", 22 | "mapeditor-remove-button": "తీసివేయి", 23 | "mapeditor-import-button2": "దిగుమతించు", 24 | "mapeditor-link-title-switcher-link-text": "లింకు", 25 | "mapeditor-form-field-title": "శీర్షిక", 26 | "mapeditor-form-field-text": "పాఠ్యం", 27 | "mapeditor-form-field-link": "లింకు", 28 | "mapeditor-form-field-icon": "ప్రతీకం", 29 | "mapeditor-form-field-image": "బొమ్మ", 30 | "semanticmaps-unrecognizeddistance": "$1 విలువ సరైన దూరం కాదు.", 31 | "semanticmaps-kml-link": "KML ఫైలు చూడండి", 32 | "semanticmaps-default-kml-pagelink": "$1 పేజీని చూడు", 33 | "semanticmaps-latitude": "అక్షాంశం: $1", 34 | "semanticmaps-longitude": "రేఖాంశం: $1", 35 | "semanticmaps-altitude": "సముద్రమట్టం: $1", 36 | "semanticmaps-forminput-locations": "ప్రాంతాలు", 37 | "semanticmaps-kml-title": "ఫలితాలకు అప్రమేయంగా వచ్చే పేరు", 38 | "maps-validator-type-string": "పాఠ్యం", 39 | "maps-validator-type-integer": "పూర్ణ సంఖ్య", 40 | "maps-validator-type-float": "సంఖ్య", 41 | "maps-validator-type-boolean": "అవును/కాదు", 42 | "maps-validator-describe-header-parameter": "పరామితి", 43 | "maps-validator-describe-header-type": "రకం", 44 | "maps-validator-describe-header-default": "అప్రమేయం", 45 | "maps-validator-describe-header-description": "వివరణ", 46 | "maps-validator-describe-required": "తప్పనిసరి" 47 | } 48 | -------------------------------------------------------------------------------- /i18n/th.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Aefgh39622", 5 | "Ans", 6 | "Geonuch", 7 | "Nitisart Jungtrakungrat", 8 | "Prem4826", 9 | "Woraponboonkerd" 10 | ] 11 | }, 12 | "maps-desc": "ให้ความสามารถในการแสดงพิกัดในแผนที่ และที่อยู่ที่เป็นรหัสทางภูมิศาสตร์([http://mapping.referata.com/wiki/Maps_examples demo]).\n
บริการแผนที่ที่มีอยู่: $1", 13 | "maps_map": "แผนที่", 14 | "maps-markers": "ตัวทำเครื่องหมาย", 15 | "maps-others": "อื่น ๆ", 16 | "maps-ns-layer": "เลเยอร์", 17 | "maps-layer-property": "คุณสมบัติ", 18 | "maps-layer-value": "ค่า", 19 | "maps-layer-errors": "ข้อผิดพลาด", 20 | "maps-layer-of-type": "เลเยอร์ของประเภท \"$1\"", 21 | "maps-abb-north": "น", 22 | "maps-abb-east": "ตอ", 23 | "maps-abb-south": "ต", 24 | "maps-abb-west": "ตต", 25 | "maps-latitude": "ละติจูด:", 26 | "maps-longitude": "ลองจิจูด:", 27 | "maps_coordinates_missing": "ไม่ได้กำหนดพิกัดของแผนที่มาให้", 28 | "maps_map_cannot_be_displayed": "ไม่สามารถแสดงแผนที่ได้", 29 | "maps_click_to_activate": "คลิกเพื่อเปิดใช้งานแผนที่", 30 | "mapeditor-none-text": "ไม่มี", 31 | "mapeditor-done-button": "เสร็จสิ้น", 32 | "mapeditor-remove-button": "เอาออก", 33 | "mapeditor-import-button2": "นำเข้า", 34 | "mapeditor-export-button": "ส่งออกเป็นโค้ดวิกิ", 35 | "mapeditor-import-button": "นำเข้าจากโค้ดวิกิ", 36 | "mapeditor-clear-button": "ล้างแผนที่", 37 | "mapeditor-code-title": "โค้ดวิกิ", 38 | "mapeditor-form-title": "แก้ไขรายละเอียด", 39 | "mapeditor-link-title-switcher-link-text": "ลิงก์", 40 | "mapeditor-form-field-title": "ชื่อเรื่อง", 41 | "mapeditor-form-field-text": "ข้อความ", 42 | "mapeditor-form-field-link": "ลิงก์", 43 | "mapeditor-form-field-icon": "ไอคอน", 44 | "mapeditor-form-field-group": "กลุ่ม", 45 | "mapeditor-form-field-image": "ภาพ", 46 | "semanticmaps-forminput-locations": "ที่ตั้ง", 47 | "maps-json-editor-toolbar-clear-text": "ลบทั้งหมด" 48 | } 49 | -------------------------------------------------------------------------------- /i18n/tzm.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Tifinaghes" 5 | ] 6 | }, 7 | "maps_map": "ⵜⴰⴽⵔⵟⴰ" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/ur.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Tahir mq", 5 | "පසිඳු කාවින්ද" 6 | ] 7 | }, 8 | "maps_map": "نقشہ", 9 | "maps-loading-map": "نقشہ لوڈ ہو رہا ہے...", 10 | "maps-markers": "مارکر", 11 | "maps-searchmarkers-text": "فلٹر مارکر", 12 | "maps-others": "دوسروں", 13 | "maps-ns-layer": "تہہ", 14 | "maps-layer-property": "پراپرٹی", 15 | "maps-layer-value": "قدر", 16 | "maps-layer-errors": "غلطیاں", 17 | "maps-mapsdoc-par-service": "کے لئے پیرامیٹر کی دستاویزات کو ظاہر کرنے کے لئے تعریفیں سروس.", 18 | "maps-coordinates-par-location": "نقاط آپ تشکيل کرنے کے لئے چاہتے ہیں.", 19 | "maps-distance-par-distance": "ایک مخصوص یونٹ کے ساتھ اس کے برابر کرنے کے لئے تبدیل کرنے کے لئے مسافت.", 20 | "maps-latitude": "عرض البلد:", 21 | "maps-longitude": "طول البلد:", 22 | "maps_map_cannot_be_displayed": "نقشے کو دکھایا جائے نہیں کر سکتے ہیں.", 23 | "maps_click_to_activate": "نقشہ کو چالو کرنے کے لئے کلک کریں", 24 | "maps-googlemaps3-par-type": "ابتدائی طور پر ظاہر کرنے کے لئے نقشہ قسم.", 25 | "maps-googlemaps3-par-controls": "نقشے پر جگہ کنٹرول.", 26 | "maps-googlemaps3-par-zoomstyle": "زوم کے کنٹرول کے انداز.", 27 | "maps-googlemaps3-par-typestyle": "قسم کے کنٹرول کے انداز.", 28 | "maps-googlemaps3-par-poi": "دلچسپی کے پوائنٹس دکھائیں ۔", 29 | "mapeditor": "ایڈیٹر کا نقشہ", 30 | "mapeditor-none-text": "کوئی بھی نہیں", 31 | "mapeditor-done-button": "کیا کیا", 32 | "mapeditor-remove-button": "حذف کریں", 33 | "mapeditor-import-button2": "درآمد", 34 | "mapeditor-clear-button": "واضح نقشہ", 35 | "mapeditor-form-title": "تفصیلات میں ترمیم کریں", 36 | "mapeditor-form-field-title": "عنوان", 37 | "mapeditor-form-field-text": "ٹیکسٹ", 38 | "mapeditor-form-field-link": "لنک", 39 | "mapeditor-form-field-group": "گروپ", 40 | "mapeditor-form-field-image": "تصویر", 41 | "semanticmaps-forminput-locations": "مقامات", 42 | "maps-validator-type-string": "ٹیکسٹ", 43 | "maps-validator-type-char": "کردار", 44 | "maps-validator-describe-header-type": "قسم", 45 | "maps-validator-describe-header-default": "پہلے سے طے شدہ", 46 | "maps-validator-describe-header-description": "تفصیل", 47 | "maps-validator-describe-empty": "خالی" 48 | } 49 | -------------------------------------------------------------------------------- /i18n/vep.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [] 4 | }, 5 | "maps-abb-north": "Pohj.", 6 | "maps-abb-east": "Päivl.", 7 | "maps-abb-south": "Suvi", 8 | "maps-abb-west": "Päivn.", 9 | "maps-latitude": "Leveduz:", 10 | "maps-longitude": "Piduz:" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/vo.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [] 4 | }, 5 | "maps-abb-north": "N", 6 | "maps-abb-east": "L", 7 | "maps-abb-south": "S", 8 | "maps-abb-west": "V", 9 | "maps-latitude": "Videt:", 10 | "maps-longitude": "Lunet:" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/yi.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Pacha Tchernof", 5 | "פוילישער", 6 | "පසිඳු කාවින්ද" 7 | ] 8 | }, 9 | "maps-others": "אַנדערע", 10 | "maps-layer-value": "ווערט", 11 | "maps-layer-errors": "פֿעלערן", 12 | "mapeditor-remove-button": "אַראָפּנעמען", 13 | "mapeditor-import-button2": "אימפארט", 14 | "mapeditor-link-title-switcher-link-text": "לינק", 15 | "mapeditor-form-field-text": "טעקסט", 16 | "mapeditor-form-field-link": "לינק", 17 | "mapeditor-form-field-group": "גרופּע", 18 | "mapeditor-form-field-image": "בילד", 19 | "maps-json-editor-toolbar-cancel-text": "אַנולירן", 20 | "maps-validator-type-string": "טעקסט", 21 | "maps-validator-describe-header-description": "באַשרײַבונג", 22 | "maps-validator-describe-empty": "ליידיק" 23 | } 24 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | src/ 4 | tests/ 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /phpstan-baseline.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - 4 | message: "#^Call to static method getInstance\\(\\) on an unknown class SMW\\\\Services\\\\ServicesFactory\\.$#" 5 | count: 1 6 | path: src/MapsFactory.php 7 | 8 | - 9 | message: "#^Method Maps\\\\MapsFactory\\:\\:getSmwFactory\\(\\) has invalid return type SMW\\\\Services\\\\ServicesFactory\\.$#" 10 | count: 1 11 | path: src/MapsFactory.php 12 | 13 | - 14 | message: "#^Access to constant PRINT_THIS on an unknown class SMW\\\\Query\\\\PrintRequest\\.$#" 15 | count: 1 16 | path: src/MapsHooks.php 17 | 18 | - 19 | message: "#^Call to an undefined static method MediaWiki\\\\Settings\\\\SettingsBuilder\\:\\:getInstance\\(\\)\\.$#" 20 | count: 1 21 | path: src/MapsHooks.php 22 | 23 | - 24 | message: "#^Call to static method newFromExternalLink\\(\\) on an unknown class AlItem\\.$#" 25 | count: 1 26 | path: src/MapsHooks.php 27 | 28 | - 29 | message: "#^Parameter \\$admin_links_tree of method Maps\\\\MapsHooks\\:\\:addToAdminLinks\\(\\) has invalid type ALTree\\.$#" 30 | count: 1 31 | path: src/MapsHooks.php 32 | 33 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - phpstan-baseline.neon 3 | 4 | parameters: 5 | level: 1 6 | paths: 7 | - src 8 | excludePaths: 9 | - src/SemanticMW/ 10 | - src/SemanticMapsSetup.php 11 | - src/MapsSetup.php 12 | - src/Map/CargoFormat/ 13 | - src/Map/SemanticFormat/ 14 | 15 | - src/GeoJsonPages/ 16 | - src/LegacyMapEditor/ 17 | scanDirectories: 18 | - ../../includes 19 | - ../../tests/phpunit 20 | - ../../vendor 21 | ignoreErrors: 22 | - '#Constant [a-zA-Z0-9\\_]+ not found#' 23 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | tests/Unit 16 | 17 | 18 | tests/Integration 19 | 20 | 21 | tests/System 22 | 23 | 24 | 25 | 26 | src 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /resources/GoogleMaps/ext.maps.googlemaps3.js: -------------------------------------------------------------------------------- 1 | window.mapsGoogleList = []; 2 | 3 | (function( $, mw ) { 4 | mw.hook( 'wikipage.content' ).add( function ( $content ) { 5 | if( typeof google === 'undefined' ) { 6 | $content.find( '.maps-googlemaps3' ).text( mw.msg( 'maps-googlemaps3-incompatbrowser' ) ); 7 | } else { 8 | $content.find( '.maps-googlemaps3' ).each( function() { 9 | var $this = $( this ); 10 | window.mapsGoogleList.push( 11 | $this.googlemaps( $this.data( 'mw-maps-mapdata' ) || {} ) 12 | ); 13 | } ); 14 | } 15 | } ); 16 | })( window.jQuery, window.mediaWiki ); 17 | -------------------------------------------------------------------------------- /resources/GoogleMaps/geoxml3/README: -------------------------------------------------------------------------------- 1 | geoxml3.js and ZipFile.complete.js has been fetched from the googlecode project : http://code.google.com/p/geoxml3/ 2 | from the branch kmz (which seems to be the branch containing the most features) 3 | http://code.google.com/p/geoxml3/source/browse/#svn%2Fbranches%2Fkmz 4 | 5 | The latest version fetched when writing this was https://github.com/geocodezip/geoxml3/tree/a07796fa205d9d40a3c6f898a44324245935984f. 6 | -------------------------------------------------------------------------------- /resources/GoogleMaps/gm3-util-library/README: -------------------------------------------------------------------------------- 1 | ==markerclusterer.js and markerwithlabel.js== 2 | Is fetched from trunk @ https://github.com/googlemaps/v3-utility-library 3 | 4 | On update don't forget to add the following line at the end of the file: 5 | 6 | window.MarkerClusterer = MarkerClusterer; 7 | -------------------------------------------------------------------------------- /resources/GoogleMaps/gm3-util-library/markerwithlabel.css: -------------------------------------------------------------------------------- 1 | .markerwithlabel { 2 | color: black; 3 | background-color: white; 4 | font-family: "Lucida Grande", "Arial", sans-serif; 5 | font-size: 12px; 6 | text-align: center; 7 | white-space: nowrap; 8 | padding: 3px; 9 | border-radius: 4px; 10 | -moz-border-radius: 4px; 11 | -webkit-border-radius: 4px; 12 | border: 1px solid darkred; 13 | } -------------------------------------------------------------------------------- /resources/GoogleMaps/googlemaps3ajax.js: -------------------------------------------------------------------------------- 1 | /** 2 | * JavaScript for Google Maps v3 maps in the Semantic Maps extension. 3 | * @see https://www.mediawiki.org/wiki/Extension:Semantic_Maps 4 | * 5 | * @licence GNU GPL v2+ 6 | * @author Peter Grassberger < petertheone@gmail.com > 7 | */ 8 | 9 | 10 | (function( $, sm ) { 11 | var ajaxRequest = null; 12 | var mapEvents = ['dragend', 'bounds_changed']; 13 | 14 | $( document ).ready( function() { 15 | // todo: find a way to remove setTimeout. 16 | setTimeout( function() { 17 | if( typeof google === 'undefined' ) { 18 | return; 19 | } 20 | $( window.mapsGoogleList ).each( function( index, map ) { 21 | if( !map.options.ajaxquery || !map.options.ajaxcoordproperty ) { 22 | return; 23 | } 24 | $( mapEvents ).each( function( index, event ) { 25 | google.maps.event.addListener( map.map, event, function() { 26 | var bounds = map.map.getBounds(); 27 | var query = sm.buildQueryString( 28 | decodeURIComponent( map.options.ajaxquery.replace( /\+/g, ' ' ) ), 29 | map.options.ajaxcoordproperty, 30 | bounds.getNorthEast().lat(), 31 | bounds.getNorthEast().lng(), 32 | bounds.getSouthWest().lat(), 33 | bounds.getSouthWest().lng() 34 | ); 35 | 36 | if( ajaxRequest !== null ) { 37 | ajaxRequest.abort(); 38 | } 39 | ajaxRequest = sm.ajaxUpdateMarker( map, query, map.options.icon ).done( function() { 40 | map.createMarkerCluster(); 41 | ajaxRequest = null; 42 | } ); 43 | } ); 44 | } ); 45 | } ); 46 | }, 500 ); 47 | } ); 48 | })( window.jQuery, window.sm ); 49 | -------------------------------------------------------------------------------- /resources/GoogleMaps/img/blue-dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/GoogleMaps/img/blue-dot.png -------------------------------------------------------------------------------- /resources/GoogleMaps/img/mylocation-sprite-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/GoogleMaps/img/mylocation-sprite-2x.png -------------------------------------------------------------------------------- /resources/MapSaver.js: -------------------------------------------------------------------------------- 1 | (function(mw) { 2 | 'use strict'; 3 | 4 | function getUserHasPermission(permission, callback) { 5 | mw.user.getRights( 6 | function(rights) { 7 | callback(rights.includes(permission)) 8 | } 9 | ); 10 | } 11 | 12 | function ifUserHasPermission(permission, callback) { 13 | getUserHasPermission( 14 | permission, 15 | function(hasPermission) { 16 | if (hasPermission) { 17 | callback(); 18 | } 19 | } 20 | ); 21 | } 22 | 23 | let MapSaver = function(pageName) { 24 | let self = {}; 25 | 26 | // parameters.newContent: required string 27 | // parameters.summary: required string 28 | // parameters.done: required callback function 29 | self.save = function(paremeters) { 30 | new mw.Api().edit( 31 | pageName, 32 | function(revision) { 33 | let editApiParameters = { 34 | text: paremeters.newContent, 35 | summary: paremeters.summary, 36 | minor: false 37 | }; 38 | 39 | ifUserHasPermission( 40 | "applychangetags", 41 | function() { 42 | editApiParameters.tags = ['maps-visual-edit']; 43 | } 44 | ); 45 | 46 | return editApiParameters; 47 | } 48 | ).then(paremeters.done); 49 | }; 50 | 51 | return self; 52 | }; 53 | 54 | if (!window.maps) {window.maps = {};} 55 | 56 | window.maps.MapSaver = MapSaver; 57 | })(window.mediaWiki); 58 | -------------------------------------------------------------------------------- /resources/WikitextEditor/css/jquery.miniColors.css: -------------------------------------------------------------------------------- 1 | .miniColors-trigger { 2 | height: 22px; 3 | width: 22px; 4 | /* @embed */ 5 | background: url(../images/trigger.png) center no-repeat; 6 | vertical-align: middle; 7 | margin: 0 .25em; 8 | display: inline-block; 9 | outline: none; 10 | } 11 | 12 | .miniColors-selector { 13 | position: absolute; 14 | width: 175px; 15 | height: 150px; 16 | background: #FFF; 17 | border: solid 1px #BBB; 18 | -moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25); 19 | -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25); 20 | box-shadow: 0 0 6px rgba(0, 0, 0, .25); 21 | -moz-border-radius: 5px; 22 | -webkit-border-radius: 5px; 23 | border-radius: 5px; 24 | padding: 5px; 25 | z-index: 999999; 26 | } 27 | 28 | .miniColors-selector.black { 29 | background: #000; 30 | border-color: #000; 31 | } 32 | 33 | .miniColors-colors { 34 | position: absolute; 35 | top: 5px; 36 | left: 5px; 37 | width: 150px; 38 | height: 150px; 39 | /* @embed */ 40 | background: url(../images/gradient.png) center no-repeat; 41 | cursor: crosshair; 42 | } 43 | 44 | .miniColors-hues { 45 | position: absolute; 46 | top: 5px; 47 | left: 160px; 48 | width: 20px; 49 | height: 150px; 50 | /* @embed */ 51 | background: url(../images/rainbow.png) center no-repeat; 52 | cursor: crosshair; 53 | } 54 | 55 | .miniColors-colorPicker { 56 | position: absolute; 57 | width: 11px; 58 | height: 11px; 59 | /* @embed */ 60 | background: url(../images/circle.gif) center no-repeat; 61 | } 62 | 63 | .miniColors-huePicker { 64 | position: absolute; 65 | left: -3px; 66 | width: 26px; 67 | height: 3px; 68 | /* @embed */ 69 | background: url(../images/line.gif) center no-repeat; 70 | } -------------------------------------------------------------------------------- /resources/WikitextEditor/css/mapeditor.css: -------------------------------------------------------------------------------- 1 | #code-output-container, #code-input-container,#code-input { 2 | height: 100% !important; 3 | width: 100%; 4 | } 5 | 6 | #code-output-container { 7 | overflow: visible; 8 | } 9 | 10 | textarea#code-input, textarea#code-output { 11 | resize: none; 12 | } 13 | 14 | #code-output { 15 | -webkit-box-sizing: border-box; 16 | -moz-box-sizing: border-box; 17 | box-sizing: border-box; 18 | border: none; 19 | padding: 10px; 20 | } 21 | 22 | #map-canvas { 23 | width: 100%; 24 | height: 500px; 25 | } 26 | 27 | #map-canvas,#code-input { 28 | display: block; 29 | } 30 | 31 | .mapeditor-dialog-form fieldset label{ 32 | display: block; 33 | } 34 | 35 | .mapeditor-dialog-form fieldset input{ 36 | width: 200px; 37 | } 38 | 39 | .mapeditor-dialog-form fieldset input[type="checkbox"]{ 40 | width: auto; 41 | } 42 | 43 | .mapeditor-dialog-form fieldset input[name="strokeColor"],.mapeditor-dialog-form fieldset input[name="fillColor"]{ 44 | width: 100px; 45 | } 46 | 47 | .mapeditor-dialog-form .ui-slider{ 48 | margin: 5px 2px; 49 | width: 200px; 50 | } 51 | 52 | .link-title-switcher { 53 | margin: 5px; 54 | } 55 | 56 | #map-parameter-form{ 57 | text-align: center; 58 | } 59 | 60 | .mapeditor-controls{ 61 | padding: 5px; 62 | } 63 | 64 | .mapeditor-control-element{ 65 | background-color:white; 66 | border: 1px solid #717B87; 67 | cursor: pointer; 68 | text-align: center; 69 | float: left; 70 | padding: 2px; 71 | } 72 | 73 | .mapeditor-control-text{ 74 | font-family:"Arial","sans-serif"; 75 | font-size: 12px; 76 | padding-left: 4px; 77 | padding-right: 4px; 78 | font-weight: bold; 79 | } -------------------------------------------------------------------------------- /resources/WikitextEditor/images/circle.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/WikitextEditor/images/circle.gif -------------------------------------------------------------------------------- /resources/WikitextEditor/images/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/WikitextEditor/images/gradient.png -------------------------------------------------------------------------------- /resources/WikitextEditor/images/line.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/WikitextEditor/images/line.gif -------------------------------------------------------------------------------- /resources/WikitextEditor/images/rainbow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/WikitextEditor/images/rainbow.png -------------------------------------------------------------------------------- /resources/WikitextEditor/images/trigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/WikitextEditor/images/trigger.png -------------------------------------------------------------------------------- /resources/WikitextEditor/js/README: -------------------------------------------------------------------------------- 1 | == jquery.miniColors.js == 2 | Is dual licensed under the MIT / GPLv2 licenses 3 | https://github.com/claviska/jquery-miniColors -------------------------------------------------------------------------------- /resources/WikitextEditor/js/mapeditor.iefixes.js: -------------------------------------------------------------------------------- 1 | if (!Array.prototype.indexOf) 2 | { 3 | Array.prototype.indexOf = function(elt /*, from*/) 4 | { 5 | var len = this.length >>> 0; 6 | 7 | var from = Number(arguments[1]) || 0; 8 | from = (from < 0) 9 | ? Math.ceil(from) 10 | : Math.floor(from); 11 | if (from < 0) 12 | from += len; 13 | 14 | for (; from < len; from++) 15 | { 16 | if (from in this && 17 | this[from] === elt) 18 | return from; 19 | } 20 | return -1; 21 | }; 22 | } 23 | 24 | if(typeof String.prototype.trim !== 'function') { 25 | String.prototype.trim = function() { 26 | return this.replace(/^\s+|\s+$/g, ''); 27 | } 28 | } -------------------------------------------------------------------------------- /resources/api.js: -------------------------------------------------------------------------------- 1 | (function($, mw) { 2 | 'use strict'; 3 | 4 | function canEditPage(pageName) { 5 | let deferred = $.Deferred(); 6 | 7 | new mw.Api().get({ 8 | action: 'query', 9 | format: 'json', 10 | titles: pageName, 11 | prop: 'info', 12 | intestactions: 'edit' 13 | }).done(function(response) { 14 | // Next level usability in the MW API: 15 | let canEdit = response.query.pages[Object.keys(response.query.pages)[0]].actions.hasOwnProperty('edit'); 16 | deferred.resolve(canEdit); 17 | }); 18 | 19 | return deferred.promise(); 20 | } 21 | 22 | function getLatestRevision(pageName) { 23 | let deferred = $.Deferred(); 24 | 25 | new mw.Api().post({ 26 | action: 'query', 27 | prop: 'revisions', 28 | rvlimit: 1, 29 | rvprop: [ 'ids', 'content' ], 30 | titles: pageName 31 | }).done(function(response) { 32 | deferred.resolve(response.query.pages[Object.keys(response.query.pages)[0]].revisions[0]); 33 | }); 34 | 35 | return deferred.promise(); 36 | } 37 | 38 | function purgePage(pageName) { 39 | new mw.Api().post({ 40 | action: 'purge', 41 | titles: pageName 42 | }) 43 | } 44 | 45 | if (!window.maps) {window.maps = {};} 46 | 47 | window.maps.api = { 48 | canEditPage: canEditPage, 49 | getLatestRevision: getLatestRevision, 50 | purgePage: purgePage 51 | }; 52 | })(window.jQuery, window.mediaWiki); 53 | -------------------------------------------------------------------------------- /resources/geoJsonPage.js: -------------------------------------------------------------------------------- 1 | (function( $, mw, maps ) { 2 | 3 | function hideLoadingMessage(map, $content) { 4 | map.on( 5 | 'load', 6 | function() { 7 | $content.find('div.maps-loading-message').hide(); 8 | } 9 | ); 10 | } 11 | 12 | function addZoomControl(map) { 13 | map.addControl(new L.Control.Zoom()); 14 | } 15 | 16 | function addTitleLayer(map) { 17 | L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { 18 | attribution: '© OpenStreetMap contributors' 19 | }).addTo(map); 20 | } 21 | 22 | function fitContent(map, geoJsonLayer) { 23 | map.fitWorld(); 24 | let bounds = geoJsonLayer.getBounds(); 25 | 26 | if (bounds.isValid()) { 27 | if (bounds.getNorthEast().equals(bounds.getSouthWest())) { 28 | map.setView( 29 | bounds.getCenter(), 30 | 14 31 | ); 32 | } 33 | else { 34 | map.fitBounds(bounds); 35 | } 36 | } 37 | } 38 | 39 | function initializeWithEditor(map) { 40 | let editor = maps.leaflet.LeafletEditor( 41 | map, 42 | new maps.MapSaver(mw.config.get('wgPageName')) 43 | ); 44 | 45 | editor.onSaved(function() { 46 | alert(mw.msg('maps-json-editor-changes-saved')); 47 | }); 48 | 49 | editor.initialize(window.GeoJson); 50 | 51 | fitContent(map, editor.getLayer()); 52 | } 53 | 54 | function initializePlainMap(map) { 55 | fitContent( 56 | map, 57 | maps.leaflet.GeoJson.newGeoJsonLayer(L, window.GeoJson).addTo(map) 58 | ); 59 | } 60 | 61 | function initializeGeoJsonAndEditorUi(map) { 62 | if (mw.config.get('wgCurRevisionId') === mw.config.get('wgRevisionId')) { 63 | 64 | maps.api.canEditPage(mw.config.get('wgPageName')).done( 65 | function(canEdit) { 66 | if (canEdit) { 67 | initializeWithEditor(map); 68 | } 69 | else { 70 | initializePlainMap(map); 71 | } 72 | } 73 | ); 74 | } 75 | else { 76 | initializePlainMap(map); 77 | } 78 | } 79 | 80 | mw.hook( 'wikipage.content' ).add( function ( $content ) { 81 | let map = L.map( 82 | 'GeoJsonMap', 83 | { 84 | fullscreenControl: true, 85 | fullscreenControlOptions: {position: 'topright'}, 86 | zoomControl: false 87 | } 88 | ); 89 | 90 | hideLoadingMessage(map, $content); 91 | addZoomControl(map); 92 | addTitleLayer(map); 93 | initializeGeoJsonAndEditorUi(map); 94 | } ); 95 | 96 | })( window.jQuery, window.mediaWiki, window.maps ); 97 | -------------------------------------------------------------------------------- /resources/geojson.new.page.js: -------------------------------------------------------------------------------- 1 | $( document ).ready( function() { 2 | function getErrorMessageForFailure(failureReason) { 3 | if (failureReason === 'assertuserfailed') { 4 | return 'Could not create page because your session expired. The page will be reloaded'; 5 | } 6 | 7 | return 'Failed to create the page: ' + failureReason; 8 | } 9 | 10 | $('#maps-geojson-new').click( 11 | function() { 12 | $(this).prop('disabled', true); 13 | $(this).text(mw.msg('maps-geo-json-create-page-creating')); 14 | 15 | new mw.Api().create( 16 | mw.config.get('wgPageName'), 17 | { 18 | summary: mw.msg('maps-geo-json-create-page-summary') 19 | }, 20 | '{"type": "FeatureCollection", "features": []}' 21 | ).then( 22 | function(editData) { 23 | if (editData.result !== 'Success') { 24 | console.log(editData); 25 | alert('Failed to create the page'); 26 | } 27 | 28 | location.reload(); 29 | } 30 | ).fail( 31 | function(reason) { 32 | alert(getErrorMessageForFailure(reason)); 33 | location.reload(); 34 | } 35 | ); 36 | } 37 | ); 38 | } ); 39 | -------------------------------------------------------------------------------- /resources/leaflet/GeoJson.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | let GeoJson = {}; 5 | 6 | // https://github.com/Leaflet/Leaflet/blob/f8e09f993292579a1af88261c9b461730f22e4e6/src/layer/GeoJSON.js#L49-L57 7 | // https://github.com/mapbox/simplestyle-spec/tree/master/1.1.0 8 | // https://leafletjs.com/reference-1.6.0.html#path 9 | GeoJson.simpleStyleToLeafletPathOptions = function(featureProperties) { 10 | let simpleStyleToLeaflet = { 11 | 'stroke': 'color', 12 | 'stroke-width': 'weight', 13 | 'stroke-opacity': 'opacity', 14 | 'fill': 'fillColor', 15 | 'fill-opacity': 'fillOpacity', 16 | }; 17 | 18 | let pathOptions = {}; 19 | 20 | for (let [key, value] of Object.entries(simpleStyleToLeaflet)) { 21 | if (featureProperties[key]) { 22 | pathOptions[value] = featureProperties[key]; 23 | } 24 | } 25 | 26 | return pathOptions; 27 | }; 28 | 29 | function escapeHTML(unsafeText) { 30 | let div = document.createElement('div'); 31 | div.innerText = unsafeText; 32 | return div.innerHTML; 33 | } 34 | 35 | GeoJson.popupContentFromProperties = function(properties) { 36 | if (!properties.title && !properties.description) { 37 | return ''; 38 | } 39 | 40 | if (!properties.description) { 41 | return escapeHTML(properties.title); 42 | } 43 | 44 | if (!properties.title) { 45 | return escapeHTML(properties.description); 46 | } 47 | 48 | return '' + escapeHTML(properties.title) + '
' 49 | + escapeHTML(properties.description || ''); 50 | }; 51 | 52 | GeoJson.newGeoJsonLayer = function(L, json) { 53 | return L.geoJSON( 54 | json, 55 | { 56 | style: function (feature) { 57 | return GeoJson.simpleStyleToLeafletPathOptions(feature.properties); 58 | }, 59 | onEachFeature: function (feature, layer) { 60 | let popupContent = GeoJson.popupContentFromProperties(feature.properties); 61 | if (popupContent !== '') { 62 | layer.bindPopup(popupContent); 63 | } 64 | } 65 | } 66 | ); 67 | }; 68 | 69 | if (!window.maps) {window.maps = {};} 70 | if (!window.maps.leaflet) {window.maps.leaflet = {};} 71 | 72 | window.maps.leaflet.GeoJson = GeoJson; 73 | })(); 74 | -------------------------------------------------------------------------------- /resources/leaflet/LeafletCluster.js: -------------------------------------------------------------------------------- 1 | (function($, mw, L) { 2 | 'use strict'; 3 | 4 | if (!window.maps) {window.maps = {};} 5 | if (!window.maps.leaflet) {window.maps.leaflet = {};} 6 | 7 | window.maps.leaflet.LeafletCluster = { 8 | newLayer: function(options) { 9 | return new L.MarkerClusterGroup({ 10 | maxClusterRadius: options.clustermaxradius, 11 | disableClusteringAtZoom: options.clustermaxzoom + 1, 12 | zoomToBoundsOnClick: options.clusterzoomonclick, 13 | spiderfyOnMaxZoom: options.clusterspiderfy, 14 | iconCreateFunction: function(cluster) { 15 | var childCount = cluster.getChildCount(); 16 | 17 | var imagePath = mw.config.get( 'egMapsScriptPath' ) + '/resources/leaflet/cluster/'; 18 | 19 | var styles = [ 20 | { 21 | iconUrl: imagePath + 'm1.png', 22 | iconSize: [53, 52] 23 | }, 24 | { 25 | iconUrl: imagePath + 'm2.png', 26 | iconSize: [56, 55] 27 | }, 28 | { 29 | iconUrl: imagePath + 'm3.png', 30 | iconSize: [66, 65] 31 | }, 32 | { 33 | iconUrl: imagePath + 'm4.png', 34 | iconSize: [78, 77] 35 | }, 36 | { 37 | iconUrl: imagePath + 'm5.png', 38 | iconSize: [90, 89] 39 | } 40 | ]; 41 | 42 | var index = 0; 43 | var dv = childCount; 44 | while (dv !== 0) { 45 | dv = parseInt(dv / 10, 10); 46 | index++; 47 | } 48 | var index = Math.min(index, styles.length); 49 | index = Math.max(0, index - 1); 50 | index = Math.min(styles.length - 1, index); 51 | var style = styles[index]; 52 | 53 | return new L.divIcon({ 54 | iconSize: style.iconSize, 55 | className: '', 56 | html: '' + 58 | '' + childCount + '' 64 | }); 65 | } 66 | }); 67 | } 68 | }; 69 | })(window.jQuery, window.mediaWiki, window.L); 70 | -------------------------------------------------------------------------------- /resources/leaflet/LeafletLoader.js: -------------------------------------------------------------------------------- 1 | window.mapsLeafletList = []; 2 | 3 | (function( $, mw ) { 4 | function initializeMaps( $content ) { 5 | $content.find( '.maps-leaflet' ).each( function() { 6 | let $this = $( this ); 7 | 8 | if ( $this.data( 'initialized' ) ) { 9 | return; 10 | } 11 | 12 | $this.data( 'initialized', true ); 13 | 14 | let jqueryMap = $this.leafletmaps( $this.data( 'mw-maps-mapdata' ) || {} ); 15 | 16 | jqueryMap.setup(); 17 | 18 | window.mapsLeafletList.push(jqueryMap); 19 | } ); 20 | } 21 | 22 | mw.hook( 'wikipage.content' ).add( initializeMaps ); 23 | 24 | mw.hook( 've.activationComplete' ).add( function() { 25 | let surface = ve.init.target.getSurface(); 26 | // initializeMaps( $( surface.$element[0] ) ); 27 | 28 | let thread = setInterval( 29 | function() { 30 | initializeMaps( $( surface.$element[0] ) ); 31 | }, 32 | 1000 33 | ); 34 | 35 | // mw.hook( 've.deactivationComplete' ).add( function() { 36 | // clearInterval( thread ); 37 | // } ); 38 | 39 | // surface.getModel().on( 'history', function() { 40 | // console.log('history'); 41 | // initializeMaps( $( surface.$element[0] ) ); 42 | // } ); 43 | } ); 44 | 45 | })( window.jQuery, window.mediaWiki ); 46 | -------------------------------------------------------------------------------- /resources/leaflet/cluster/m1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/leaflet/cluster/m1.png -------------------------------------------------------------------------------- /resources/leaflet/cluster/m2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/leaflet/cluster/m2.png -------------------------------------------------------------------------------- /resources/leaflet/cluster/m3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/leaflet/cluster/m3.png -------------------------------------------------------------------------------- /resources/leaflet/cluster/m4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/leaflet/cluster/m4.png -------------------------------------------------------------------------------- /resources/leaflet/cluster/m5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/leaflet/cluster/m5.png -------------------------------------------------------------------------------- /resources/leaflet/imagePath.js: -------------------------------------------------------------------------------- 1 | L.Icon.Default.imagePath = mw.config.get( 'egMapsScriptPath' ) + '/resources/lib/leaflet/images/'; 2 | -------------------------------------------------------------------------------- /resources/leaflet/images/edit-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/leaflet/images/save-solid.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/lib/leaflet.EasyButton/easy-button.css: -------------------------------------------------------------------------------- 1 | .leaflet-bar button, 2 | .leaflet-bar button:hover { 3 | background-color: #fff; 4 | border: none; 5 | border-bottom: 1px solid #ccc; 6 | width: 26px; 7 | height: 26px; 8 | line-height: 26px; 9 | display: block; 10 | text-align: center; 11 | text-decoration: none; 12 | color: black; 13 | } 14 | 15 | .leaflet-bar button { 16 | background-position: 50% 50%; 17 | background-repeat: no-repeat; 18 | overflow: hidden; 19 | display: block; 20 | } 21 | 22 | .leaflet-bar button:hover { 23 | background-color: #f4f4f4; 24 | } 25 | 26 | .leaflet-bar button:first-of-type { 27 | border-top-left-radius: 4px; 28 | border-top-right-radius: 4px; 29 | } 30 | 31 | .leaflet-bar button:last-of-type { 32 | border-bottom-left-radius: 4px; 33 | border-bottom-right-radius: 4px; 34 | border-bottom: none; 35 | } 36 | 37 | .leaflet-bar.disabled, 38 | .leaflet-bar button.disabled { 39 | cursor: default; 40 | pointer-events: none; 41 | opacity: .4; 42 | } 43 | 44 | .easy-button-button .button-state{ 45 | display: block; 46 | width: 100%; 47 | height: 100%; 48 | position: relative; 49 | } 50 | 51 | 52 | .leaflet-touch .leaflet-bar button { 53 | width: 30px; 54 | height: 30px; 55 | line-height: 30px; 56 | } 57 | -------------------------------------------------------------------------------- /resources/lib/leaflet.StyleEditor/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/lib/leaflet.StyleEditor/img/icon.png -------------------------------------------------------------------------------- /resources/lib/leaflet.draw/images/spritesheet-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/lib/leaflet.draw/images/spritesheet-2x.png -------------------------------------------------------------------------------- /resources/lib/leaflet.fullscreen/Control.FullScreen.css: -------------------------------------------------------------------------------- 1 | .fullscreen-icon { background-image: url(icon-fullscreen.png); } 2 | .leaflet-retina .fullscreen-icon { background-image: url(icon-fullscreen-2x.png); background-size: 26px 26px; } 3 | /* one selector per rule as explained here : http://www.sitepoint.com/html5-full-screen-api/ */ 4 | .leaflet-container:-webkit-full-screen { width: 100% !important; height: 100% !important; z-index: 99999; } 5 | .leaflet-container:-ms-fullscreen { width: 100% !important; height: 100% !important; z-index: 99999; } 6 | .leaflet-container:full-screen { width: 100% !important; height: 100% !important; z-index: 99999; } 7 | .leaflet-container:fullscreen { width: 100% !important; height: 100% !important; z-index: 99999; } 8 | .leaflet-pseudo-fullscreen { position: fixed !important; width: 100% !important; height: 100% !important; top: 0px !important; left: 0px !important; z-index: 99999; } -------------------------------------------------------------------------------- /resources/lib/leaflet.fullscreen/icon-fullscreen-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/lib/leaflet.fullscreen/icon-fullscreen-2x.png -------------------------------------------------------------------------------- /resources/lib/leaflet.fullscreen/icon-fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/lib/leaflet.fullscreen/icon-fullscreen.png -------------------------------------------------------------------------------- /resources/lib/leaflet.fullscreen/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leaflet.fullscreen", 3 | "version": "1.6.0", 4 | "description": "Simple plugin for Leaflet that adds fullscreen button to your maps.", 5 | "main": "Control.FullScreen.js", 6 | "scripts": { 7 | "test": "eslint --config .eslintrc Control.FullScreen.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/brunob/leaflet.fullscreen.git" 12 | }, 13 | "keywords": [ 14 | "leaflet", 15 | "plugins", 16 | "maps", 17 | "fullscreen" 18 | ], 19 | "devDependencies": { 20 | "eslint": "^5.4.0" 21 | }, 22 | "author": "b_b", 23 | "license": "MIT License", 24 | "readmeFilename": "README.md" 25 | } 26 | -------------------------------------------------------------------------------- /resources/lib/leaflet.markercluster/MarkerCluster.css: -------------------------------------------------------------------------------- 1 | .leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow { 2 | -webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in; 3 | -moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in; 4 | -o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in; 5 | transition: transform 0.3s ease-out, opacity 0.3s ease-in; 6 | } 7 | 8 | .leaflet-cluster-spider-leg { 9 | /* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */ 10 | -webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in; 11 | -moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in; 12 | -o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in; 13 | transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in; 14 | } 15 | -------------------------------------------------------------------------------- /resources/lib/leaflet/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/lib/leaflet/images/layers-2x.png -------------------------------------------------------------------------------- /resources/lib/leaflet/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/lib/leaflet/images/layers.png -------------------------------------------------------------------------------- /resources/lib/leaflet/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/lib/leaflet/images/marker-icon-2x.png -------------------------------------------------------------------------------- /resources/lib/leaflet/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/lib/leaflet/images/marker-icon.png -------------------------------------------------------------------------------- /resources/lib/leaflet/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ProfessionalWiki/Maps/1ee1bc1798d873989c044fe24c3115e0e626f02a/resources/lib/leaflet/images/marker-shadow.png -------------------------------------------------------------------------------- /resources/semanticMaps.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @licence GNU GPL v2++ 3 | * @author Peter Grassberger < petertheone@gmail.com > 4 | */ 5 | window.sm = new ( function( $, mw ) { 6 | 7 | this.buildQueryString = function( query, ajaxcoordproperty, top, right, bottom, left ) { 8 | var isCompoundQuery = query.indexOf( '|' ) > -1; 9 | var queryParts = query.split( '|' ); 10 | 11 | $.each( queryParts, function( index ) { 12 | queryParts[index] += ' [[' + ajaxcoordproperty + '::+]] '; 13 | queryParts[index] += '[[' + ajaxcoordproperty + '::>' + bottom + '°, ' + left + '°]] '; 14 | queryParts[index] += '[[' + ajaxcoordproperty + '::<' + top + '°, ' + right + '°]]'; 15 | if( !isCompoundQuery ) { 16 | queryParts[index] += '|?' + ajaxcoordproperty; 17 | } else { 18 | queryParts[index] += ';?' + ajaxcoordproperty; 19 | } 20 | } ); 21 | 22 | return queryParts.join( ' | ' ); 23 | }; 24 | 25 | /** 26 | * Detects semicolons `;` not in square brackets `[]`. 27 | * 28 | * @param string 29 | * @returns {boolean} 30 | */ 31 | this.hasCompoundQuerySemicolon = function( string ) { 32 | return /;(?![^[]*])/g.test( string ); 33 | }; 34 | 35 | this.sendQuery = function( query ) { 36 | var action = this.hasCompoundQuerySemicolon( query ) ? 'compoundquery' : 'ask'; 37 | return $.ajax( { 38 | method: 'GET', 39 | url: mw.util.wikiScript( 'api' ), 40 | data: { 41 | 'action': action, 42 | 'query': query, 43 | 'format': 'json' 44 | }, 45 | dataType: 'json' 46 | } ); 47 | }; 48 | 49 | this.ajaxUpdateMarker = function( jqueryMap, query, icon ) { 50 | return this.sendQuery( query ).done( function( data ) { 51 | if( !data.hasOwnProperty( 'query' ) || 52 | !data.query.hasOwnProperty( 'results' ) ) { 53 | return; 54 | } 55 | 56 | jqueryMap.removeMarkers(); 57 | 58 | for( var property in data.query.results ) { 59 | if( data.query.results.hasOwnProperty( property ) ) { 60 | var location = data.query.results[property]; 61 | var coordinates = location.printouts[jqueryMap.options.ajaxcoordproperty][0]; 62 | var markerOptions = { 63 | lat: coordinates.lat, 64 | lon: coordinates.lon, 65 | title: location.fulltext, 66 | text: '' + location.fulltext + '', 67 | icon: icon 68 | }; 69 | 70 | jqueryMap.addMarker( markerOptions ); 71 | } 72 | } 73 | } ); 74 | }; 75 | 76 | } )( jQuery, mediaWiki ); 77 | -------------------------------------------------------------------------------- /src/DataAccess/CachingGeocoder.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class CachingGeocoder implements Geocoder { 18 | 19 | private Geocoder $geocoder; 20 | private BagOStuff $cache; 21 | private int $cacheTtl; 22 | 23 | public function __construct( Geocoder $geocoder, BagOStuff $cache, int $cacheTtl ) { 24 | $this->geocoder = $geocoder; 25 | $this->cache = $cache; 26 | $this->cacheTtl = $cacheTtl; 27 | } 28 | 29 | public function geocode( string $address ): ?LatLongValue { 30 | $key = $this->cache->makeKey( __CLASS__, $address ); 31 | 32 | $coordinates = $this->cache->get( $key ); 33 | 34 | // There was no entry in the cache, so we retrieve the coordinates 35 | if ( $coordinates === false ) { 36 | $coordinates = $this->geocoder->geocode( $address ); 37 | 38 | $this->cache->set( $key, $coordinates, $this->cacheTtl ); 39 | } 40 | 41 | return $coordinates; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/DataAccess/GeoJsonFetcherResult.php: -------------------------------------------------------------------------------- 1 | content = $content; 15 | $this->revisionId = $revisionId; 16 | $this->source = $source; 17 | } 18 | 19 | public function getContent(): array { 20 | return $this->content; 21 | } 22 | 23 | public function getTitleValue(): ?\TitleValue { 24 | return $this->source; 25 | } 26 | 27 | public function getRevisionId(): ?int { 28 | return $this->revisionId; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/DataAccess/Image.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class MapsFileFetcher implements FileFetcher { 16 | 17 | public function fetchFile( string $fileUrl ): string { 18 | $result = MediaWikiServices::getInstance()->getHttpRequestFactory()->get( $fileUrl ); 19 | 20 | if ( !is_string( $result ) ) { 21 | throw new FileFetchingException( $fileUrl ); 22 | } 23 | 24 | return $result; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/DataAccess/MediaWikiFileUrlFinder.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class MediaWikiFileUrlFinder implements FileUrlFinder { 15 | 16 | public function getUrlForFileName( string $fileName ): string { 17 | $colonPosition = strpos( $fileName, ':' ); 18 | 19 | $titleWithoutPrefix = $colonPosition === false ? $fileName : substr( $fileName, $colonPosition + 1 ); 20 | 21 | $file = MediaWikiServices::getInstance()->getRepoGroup()->findFile( trim( $titleWithoutPrefix ) ); 22 | 23 | if ( $file && $file->exists() ) { 24 | return $file->getURL(); 25 | } 26 | 27 | return trim( $fileName ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/DataAccess/MwImage.php: -------------------------------------------------------------------------------- 1 | file = $file; 15 | } 16 | 17 | public function getUrl(): string { 18 | return $this->file->getUrl(); 19 | } 20 | 21 | public function getWidthInPx(): int { 22 | $width = $this->file->getWidth(); 23 | return $width === false ? 0 : $width; 24 | } 25 | 26 | public function getHeightInPx(): int { 27 | $height = $this->file->getHeight(); 28 | return $height === false ? 0 : $height; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/DataAccess/MwImageRepository.php: -------------------------------------------------------------------------------- 1 | repoGroup = $repoGroup; 15 | } 16 | 17 | public function getByName( string $imageName ): ?Image { 18 | $file = $this->repoGroup->findFile( trim( $this->getNameWithoutPrefix( $imageName ) ) ); 19 | 20 | if ( $file === false || !$file->exists() ) { 21 | return null; 22 | } 23 | 24 | return new MwImage( $file ); 25 | } 26 | 27 | private function getNameWithoutPrefix( string $imageName ): string { 28 | $colonPosition = strpos( $imageName, ':' ); 29 | 30 | return $colonPosition === false ? $imageName : substr( $imageName, $colonPosition + 1 ); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/DataAccess/PageContentFetcher.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class PageContentFetcher { 14 | 15 | private \TitleParser $titleParser; 16 | private RevisionLookup $revisionLookup; 17 | 18 | public function __construct( \TitleParser $titleParser, RevisionLookup $revisionLookup ) { 19 | $this->titleParser = $titleParser; 20 | $this->revisionLookup = $revisionLookup; 21 | } 22 | 23 | public function getPageContent( string $pageTitle, int $defaultNamespace = NS_MAIN ): ?\Content { 24 | try { 25 | $title = $this->titleParser->parseTitle( $pageTitle, $defaultNamespace ); 26 | } 27 | catch ( \MalformedTitleException $e ) { 28 | return null; 29 | } 30 | 31 | $revision = $this->revisionLookup->getRevisionByTitle( $title ); 32 | 33 | if ( $revision === null ) { 34 | return null; 35 | } 36 | 37 | // $revision->getRevisionRecord()->getContent( 'main' ); 38 | return $revision->getContent( 'main' ); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/FileUrlFinder.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | interface FileUrlFinder { 12 | 13 | /** 14 | * Resolves the url of images provided as wiki page; leaves others alone. 15 | */ 16 | public function getUrlForFileName( string $fileName ): string; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/GeoJsonPages/GeoJsonContent.php: -------------------------------------------------------------------------------- 1 | getValue() ); 17 | } 18 | 19 | public static function formatJson( $value ): string { 20 | return FormatJson::encode( $value, true, FormatJson::UTF8_OK ); 21 | } 22 | 23 | public function __construct( string $text, string $modelId = self::CONTENT_MODEL_ID ) { 24 | parent::__construct( 25 | $text, 26 | $modelId 27 | ); 28 | } 29 | 30 | public function getData(): Status { 31 | $status = parent::getData(); 32 | 33 | if ( $status->isGood() && !$this->isGeoJson( $status->getValue() ) ) { 34 | return Status::newFatal( 'Invalid GeoJson' ); 35 | } 36 | 37 | return $status; 38 | } 39 | 40 | private function isGeoJson( $json ): bool { 41 | return property_exists( $json, 'type' ) 42 | && $json->type === 'FeatureCollection' 43 | && property_exists( $json, 'features' ) 44 | && is_array( $json->features ); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/GeoJsonPages/GeoJsonContentHandler.php: -------------------------------------------------------------------------------- 1 | getContentClass(); 25 | return new $class( $class::newEmptyContentString() ); 26 | } 27 | 28 | /** 29 | * @inheritdoc 30 | */ 31 | protected function fillParserOutput( 32 | Content $content, 33 | ContentParseParams $cpoParams, 34 | ParserOutput &$parserOutput 35 | ) { 36 | '@phan-var GeoJsonContent $content'; 37 | // this method won't be called below MW_VERSION 1.38 38 | 39 | if ( $cpoParams->getGenerateHtml() && $content->isValid() ) { 40 | 41 | // display map 42 | ( GeoJsonMapPageUi::forExistingPage( GeoJsonContent::formatJson( $content->getData()->getValue() ) ) ) 43 | ->addToOutput( OutputFacade::newFromParserOutput( $parserOutput ) ); 44 | 45 | if ( MapsFactory::globalInstance()->smwIntegrationIsEnabled() ) { 46 | // @FIXME alternatively decode $this->mText in GeoJsonLegacyContent 47 | // to avoid decoding it again in SubObjectBuilder -> getSubObjectsFromGeoJson 48 | $text = json_encode( $content->getData()->getValue() ); 49 | 50 | MapsFactory::globalInstance() 51 | ->newSemanticGeoJsonStore( $parserOutput, $cpoParams->getPage() ) 52 | ->storeGeoJson( $text ); 53 | } 54 | 55 | } else { 56 | parent::fillParserOutput( $content, $cpoParams, $parserOutput ); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/GeoJsonPages/GeoJsonLegacyContent.php: -------------------------------------------------------------------------------- 1 | isValid() ) { 23 | $output->setText( '' ); 24 | return; 25 | } 26 | 27 | $this->addMapHtmlToOutput( $output ); 28 | 29 | $this->storeSemanticValues( $title, $output ); 30 | } 31 | 32 | private function addMapHtmlToOutput( ParserOutput $output ) { 33 | ( GeoJsonMapPageUi::forExistingPage( $this->beautifyJSON() ) )->addToOutput( OutputFacade::newFromParserOutput( $output ) ); 34 | } 35 | 36 | private function storeSemanticValues( Title $title, ParserOutput $output ) { 37 | if ( MapsFactory::globalInstance()->smwIntegrationIsEnabled() ) { 38 | MapsFactory::globalInstance()->newSemanticGeoJsonStore( $output, $title )->storeGeoJson( $this->mText ); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/GeoJsonPages/GeoJsonMapPageUi.php: -------------------------------------------------------------------------------- 1 | json = $json; 20 | } 21 | 22 | public function addToOutput( OutputFacade $output ) { 23 | $output->addHtml( $this->getJavascript() . $this->getHtml() ); 24 | $output->addModules( 'ext.maps.geojson.page' ); 25 | } 26 | 27 | private function getJavascript(): string { 28 | return Html::element( 29 | 'script', 30 | [], 31 | $this->getJsonJs() 32 | ); 33 | } 34 | 35 | private function getJsonJs(): string { 36 | return 'var GeoJson =' 37 | . $this->json 38 | . ';'; 39 | } 40 | 41 | private function getHtml(): string { 42 | return $this->wrapHtmlInThumbDivs( 43 | Html::rawElement( 44 | 'div', 45 | [ 46 | 'id' => 'GeoJsonMap', 47 | 'style' => 'width: 100%; height: 600px; background-color: #eeeeee; overflow: hidden;', 48 | 'class' => 'maps-map maps-leaflet maps-geojson-editor' 49 | ], 50 | Html::element( 51 | 'div', 52 | [ 53 | 'class' => 'maps-loading-message' 54 | ], 55 | wfMessage( 'maps-loading-map' )->inContentLanguage()->text() 56 | ) 57 | ) 58 | ); 59 | } 60 | 61 | private function wrapHtmlInThumbDivs( string $html ): string { 62 | return Html::rawElement( 63 | 'div', 64 | [ 65 | 'class' => 'thumb' 66 | ], 67 | Html::rawElement( 68 | 'div', 69 | [ 70 | 'class' => 'thumbinner' 71 | ], 72 | $html 73 | ) 74 | ); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/GeoJsonPages/GeoJsonNewPageUi.php: -------------------------------------------------------------------------------- 1 | output = $output; 15 | } 16 | 17 | public function addToOutput(): void { 18 | $this->output->addModules( 'ext.maps.geojson.new.page' ); 19 | 20 | $this->output->addHtml( 21 | \Html::element( 22 | 'button', 23 | [ 24 | 'id' => 'maps-geojson-new' 25 | ], 26 | wfMessage( 'maps-geo-json-create-page-button' )->inContentLanguage()->text() 27 | ) 28 | ); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/GeoJsonPages/GeoJsonStore.php: -------------------------------------------------------------------------------- 1 | parserData = $parserData; 23 | $this->subjectPage = $subjectPage; 24 | $this->smwEventDispatcher = $smwEventDispatcher; 25 | $this->subObjectBuilder = $subObjectBuilder; 26 | } 27 | 28 | public function storeGeoJson( string $geoJson ) { 29 | foreach ( $this->subObjectBuilder->getSubObjectsFromGeoJson( $geoJson ) as $subObject ) { 30 | $this->parserData->getSemanticData()->addPropertyObjectValue( 31 | new DIProperty( DIProperty::TYPE_SUBOBJECT ), 32 | new SMWDIContainer( $subObject->toContainerSemanticData( $this->subjectPage->getTitleValue() ) ) 33 | ); 34 | } 35 | 36 | $this->parserData->copyToParserOutput(); 37 | 38 | $this->smwEventDispatcher->dispatch( 39 | 'InvalidateEntityCache', 40 | [ 'title' => $this->subjectPage, 'context' => 'GeoJsonContent' ] 41 | ); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/GeoJsonPages/Semantic/SubObject.php: -------------------------------------------------------------------------------- 1 | > 18 | */ 19 | private array $values = []; 20 | 21 | public function __construct( string $name ) { 22 | $this->name = $name; 23 | } 24 | 25 | public function addPropertyValuePair( string $propertyName, \SMWDataItem $dataItem ) { 26 | $this->values[$propertyName][] = $dataItem; 27 | } 28 | 29 | public function toContainerSemanticData( TitleValue $subjectPage ): ContainerSemanticData { 30 | $container = $this->newContainerSemanticData( $subjectPage ); 31 | 32 | foreach ( $this->values as $propertyName => $dataItems ) { 33 | foreach ( $dataItems as $dataItem ) { 34 | $container->addPropertyObjectValue( 35 | new DIProperty( $propertyName ), 36 | $dataItem 37 | ); 38 | } 39 | } 40 | 41 | return $container; 42 | } 43 | 44 | private function newContainerSemanticData( TitleValue $subjectPage ): ContainerSemanticData { 45 | return new ContainerSemanticData( 46 | new DIWikiPage( 47 | $subjectPage->getDBkey(), 48 | $subjectPage->getNamespace(), 49 | $subjectPage->getInterwiki(), 50 | $this->name 51 | ) 52 | ); 53 | } 54 | 55 | public function getName(): string { 56 | return $this->name; 57 | } 58 | 59 | public function getValues(): array { 60 | return $this->values; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/GeoJsonPages/Semantic/SubObjectBuilder.php: -------------------------------------------------------------------------------- 1 | featureCollectionToSubObjects( $geoJson ) ); 31 | } 32 | 33 | private function featureCollectionToSubObjects( FeatureCollection $featureCollection ) { 34 | foreach ( $featureCollection->getFeatures() as $feature ) { 35 | $geometry = $feature->getGeometry(); 36 | 37 | if ( $geometry instanceof Point ) { 38 | yield $this->pointToSubobject( $geometry, $feature->getProperties() ?? [] ); 39 | } 40 | } 41 | } 42 | 43 | private function pointToSubobject( Point $point, array $properties ): SubObject { 44 | $subObject = new SubObject( 'Point_' . ++$this->pointCount ); 45 | 46 | $subObject->addPropertyValuePair( 47 | 'HasCoordinates', 48 | new \SMWDIGeoCoord( $point->getCoordinates()[1], $point->getCoordinates()[0] ) 49 | ); 50 | 51 | if ( array_key_exists( 'description', $properties ) ) { 52 | $subObject->addPropertyValuePair( 53 | 'HasDescription', 54 | new \SMWDIBlob( $properties['description'] ) 55 | ); 56 | } 57 | 58 | if ( array_key_exists( 'title', $properties ) ) { 59 | $subObject->addPropertyValuePair( 60 | 'HasTitle', 61 | new \SMWDIBlob( $properties['title'] ) 62 | ); 63 | } 64 | 65 | return $subObject; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/LegacyMapEditor/SpecialMapEditor.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class SpecialMapEditor extends SpecialPage { 20 | 21 | /** 22 | * @see SpecialPage::__construct 23 | * 24 | * @since 2.0 25 | */ 26 | public function __construct() { 27 | parent::__construct( 'MapEditor' ); 28 | } 29 | 30 | /** 31 | * @see SpecialPage::execute 32 | * 33 | * @since 2.0 34 | * 35 | * @param null|string $subPage 36 | */ 37 | public function execute( $subPage ) { 38 | $this->setHeaders(); 39 | 40 | $outputPage = $this->getOutput(); 41 | 42 | $outputPage->addHtml( 43 | GoogleMapsService::getApiScript( 44 | $this->getLanguage()->getCode(), 45 | [ 'libraries' => 'drawing' ] 46 | ) 47 | ); 48 | 49 | $outputPage->addModules( [ 'ext.maps.wikitext.editor' ] ); 50 | $editorHtml = new MapEditorHtml( $this->getAttribs() ); 51 | $html = $editorHtml->getEditorHtml(); 52 | $outputPage->addHTML( $html ); 53 | } 54 | 55 | /** 56 | * @since 2.1 57 | * 58 | * @return array 59 | */ 60 | protected function getAttribs() { 61 | return [ 62 | 'id' => 'map-canvas', 63 | 'context' => 'Maps\MediaWiki\Specials\SpecialMapEditor' 64 | ]; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/LegacyModel/BaseElement.php: -------------------------------------------------------------------------------- 1 | 12 | * @author Jeroen De Dauw < jeroendedauw@gmail.com > 13 | */ 14 | abstract class BaseElement { 15 | 16 | private $title; 17 | private $text; 18 | private $link; 19 | 20 | public function setTitle( string $title ) { 21 | $this->title = trim( $title ); 22 | } 23 | 24 | public function setText( string $text ) { 25 | $this->text = trim( $text ); 26 | } 27 | 28 | public function setLink( string $link ) { 29 | $this->link = $link; 30 | } 31 | 32 | public function getArrayValue() { 33 | return $this->getJSONObject(); 34 | } 35 | 36 | /** 37 | * @deprecated 38 | */ 39 | public function getJSONObject( string $defText = '', string $defTitle = '' ): array { 40 | return [ 41 | 'text' => $this->text ?? $defText, 42 | 'title' => $this->title ?? $defTitle, 43 | 'link' => $this->link ?? '', 44 | ]; 45 | } 46 | 47 | public function getText(): string { 48 | return $this->text ?? ''; 49 | } 50 | 51 | public function getTitle(): string { 52 | return $this->title ?? ''; 53 | } 54 | 55 | public function getLink(): string { 56 | return $this->link ?? ''; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/LegacyModel/BaseFillableElement.php: -------------------------------------------------------------------------------- 1 | $this->hasFillColor() ? $this->getFillColor() : '#FF0000', 19 | 'fillOpacity' => $this->hasFillOpacity() ? $this->getFillOpacity() : '0.5', 20 | ]; 21 | return array_merge( $parentArray, $array ); 22 | } 23 | 24 | public function hasFillColor() { 25 | return !is_null( $this->fillColor ) && $this->fillColor !== ''; 26 | } 27 | 28 | public function getFillColor() { 29 | return $this->fillColor; 30 | } 31 | 32 | public function setFillColor( $fillColor ) { 33 | $this->fillColor = trim( $fillColor ); 34 | } 35 | 36 | public function hasFillOpacity() { 37 | return !is_null( $this->fillOpacity ) && $this->fillOpacity !== ''; 38 | } 39 | 40 | public function getFillOpacity() { 41 | return $this->fillOpacity; 42 | } 43 | 44 | public function setFillOpacity( $fillOpacity ) { 45 | $this->fillOpacity = trim( $fillOpacity ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/LegacyModel/BaseStrokableElement.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class BaseStrokableElement extends BaseElement { 14 | 15 | protected $strokeColor; 16 | protected $strokeOpacity; 17 | protected $strokeWeight; 18 | 19 | public function getJSONObject( string $defText = '', string $defTitle = '' ): array { 20 | $parentArray = parent::getJSONObject( $defText, $defTitle ); 21 | $array = [ 22 | 'strokeColor' => $this->hasStrokeColor() ? $this->getStrokeColor() : '#FF0000', 23 | 'strokeOpacity' => $this->hasStrokeOpacity() ? $this->getStrokeOpacity() : '1', 24 | 'strokeWeight' => $this->hasStrokeWeight() ? $this->getStrokeWeight() : '2' 25 | ]; 26 | return array_merge( $parentArray, $array ); 27 | } 28 | 29 | public function hasStrokeColor() { 30 | return !is_null( $this->strokeColor ) && $this->strokeColor !== ''; 31 | } 32 | 33 | public function getStrokeColor() { 34 | return $this->strokeColor; 35 | } 36 | 37 | public function setStrokeColor( $strokeColor ) { 38 | $this->strokeColor = trim( $strokeColor ); 39 | } 40 | 41 | public function hasStrokeOpacity() { 42 | return !is_null( $this->strokeOpacity ) && $this->strokeOpacity !== ''; 43 | } 44 | 45 | public function getStrokeOpacity() { 46 | return $this->strokeOpacity; 47 | } 48 | 49 | public function setStrokeOpacity( $strokeOpacity ) { 50 | $this->strokeOpacity = trim( $strokeOpacity ); 51 | } 52 | 53 | public function hasStrokeWeight() { 54 | return !is_null( $this->strokeWeight ) && $this->strokeWeight !== ''; 55 | } 56 | 57 | public function getStrokeWeight() { 58 | return $this->strokeWeight; 59 | } 60 | 61 | public function setStrokeWeight( $strokeWeight ) { 62 | $this->strokeWeight = trim( $strokeWeight ); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/LegacyModel/Circle.php: -------------------------------------------------------------------------------- 1 | 15 | * @author Jeroen De Dauw < jeroendedauw@gmail.com > 16 | */ 17 | class Circle extends \Maps\LegacyModel\BaseFillableElement { 18 | 19 | private $circleCentre; 20 | private $circleRadius; 21 | 22 | public function __construct( LatLongValue $circleCentre, float $circleRadius ) { 23 | if ( !is_float( $circleRadius ) && !is_int( $circleRadius ) ) { 24 | throw new InvalidArgumentException( '$circleRadius must be a float or int' ); 25 | } 26 | 27 | if ( $circleRadius <= 0 ) { 28 | throw new InvalidArgumentException( '$circleRadius must be greater than zero' ); 29 | } 30 | 31 | $this->setCircleCentre( $circleCentre ); 32 | $this->setCircleRadius( $circleRadius ); 33 | } 34 | 35 | public function getJSONObject( string $defText = '', string $defTitle = '' ): array { 36 | return array_merge( 37 | parent::getJSONObject( $defText, $defTitle ), 38 | [ 39 | 'centre' => [ 40 | 'lon' => $this->getCircleCentre()->getLongitude(), 41 | 'lat' => $this->getCircleCentre()->getLatitude() 42 | ], 43 | 'radius' => intval( $this->getCircleRadius() ), 44 | ] 45 | ); 46 | } 47 | 48 | public function getCircleCentre(): LatLongValue { 49 | return $this->circleCentre; 50 | } 51 | 52 | public function setCircleCentre( LatLongValue $circleCentre ) { 53 | $this->circleCentre = $circleCentre; 54 | } 55 | 56 | public function getCircleRadius(): float { 57 | return $this->circleRadius; 58 | } 59 | 60 | public function setCircleRadius( float $circleRadius ) { 61 | $this->circleRadius = $circleRadius; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/LegacyModel/ImageOverlay.php: -------------------------------------------------------------------------------- 1 | 14 | * @author Jeroen De Dauw < jeroendedauw@gmail.com > 15 | */ 16 | class ImageOverlay extends Rectangle { 17 | 18 | private $imageUrl; 19 | 20 | public function __construct( LatLongValue $boundsNorthEast, LatLongValue $boundsSouthWest, string $image ) { 21 | parent::__construct( $boundsNorthEast, $boundsSouthWest ); 22 | 23 | $this->imageUrl = $image; 24 | } 25 | 26 | public function getImage(): string { 27 | return $this->imageUrl; 28 | } 29 | 30 | public function getJSONObject( string $defText = '', string $defTitle = '' ): array { 31 | $data = parent::getJSONObject( $defText, $defTitle ); 32 | 33 | $data['image'] = $this->imageUrl; 34 | 35 | return $data; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/LegacyModel/Line.php: -------------------------------------------------------------------------------- 1 | 18 | * @author Jeroen De Dauw < jeroendedauw@gmail.com > 19 | */ 20 | class Line extends \Maps\LegacyModel\BaseStrokableElement { 21 | 22 | /** 23 | * @since 3.0 24 | * 25 | * @var LatLongValue[] 26 | */ 27 | protected $coordinates; 28 | 29 | /** 30 | * @since 3.0 31 | * 32 | * @param LatLongValue[] $coordinates 33 | * 34 | * @throws InvalidArgumentException 35 | */ 36 | public function __construct( array $coordinates = [] ) { 37 | foreach ( $coordinates as $coordinate ) { 38 | if ( !( $coordinate instanceof LatLongValue ) ) { 39 | throw new InvalidArgumentException( 'Can only construct Line with LatLongValue objects' ); 40 | } 41 | } 42 | 43 | $this->coordinates = $coordinates; 44 | } 45 | 46 | /** 47 | * @since 3.0 48 | * 49 | * @return LatLongValue[] 50 | */ 51 | public function getLineCoordinates() { 52 | return $this->coordinates; 53 | } 54 | 55 | public function getJSONObject( string $defText = '', string $defTitle = '' ): array { 56 | $parentArray = parent::getJSONObject( $defText, $defTitle ); 57 | $posArray = []; 58 | 59 | foreach ( $this->coordinates as $mapLocation ) { 60 | $posArray[] = [ 61 | 'lat' => $mapLocation->getLatitude(), 62 | 'lon' => $mapLocation->getLongitude() 63 | ]; 64 | } 65 | 66 | $posArray = [ 'pos' => $posArray ]; 67 | 68 | return array_merge( $parentArray, $posArray ); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/LegacyModel/Polygon.php: -------------------------------------------------------------------------------- 1 | 12 | * @author Jeroen De Dauw < jeroendedauw@gmail.com > 13 | */ 14 | class Polygon extends Line { 15 | 16 | private $onlyVisibleOnHover = false; 17 | private $fillOpacity = '0.5'; 18 | private $fillColor = '#FF0000'; 19 | 20 | public function isOnlyVisibleOnHover(): bool { 21 | return $this->onlyVisibleOnHover; 22 | } 23 | 24 | public function setOnlyVisibleOnHover( bool $visible ) { 25 | $this->onlyVisibleOnHover = $visible; 26 | } 27 | 28 | public function setFillOpacity( string $fillOpacity ) { 29 | $this->fillOpacity = $fillOpacity; 30 | } 31 | 32 | public function setFillColor( string $fillColor ) { 33 | $this->fillColor = $fillColor; 34 | } 35 | 36 | public function getJSONObject( string $defText = '', string $defTitle = '' ): array { 37 | $json = parent::getJSONObject( $defText, $defTitle ); 38 | 39 | $json['onlyVisibleOnHover'] = $this->onlyVisibleOnHover; 40 | $json['fillColor'] = $this->fillColor; 41 | $json['fillOpacity'] = $this->fillOpacity; 42 | 43 | return $json; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/LegacyModel/Rectangle.php: -------------------------------------------------------------------------------- 1 | 16 | * @author Jeroen De Dauw < jeroendedauw@gmail.com > 17 | */ 18 | class Rectangle extends \Maps\LegacyModel\BaseFillableElement { 19 | 20 | /** 21 | * @since 3.0 22 | * @var LatLongValue 23 | */ 24 | protected $rectangleNorthEast; 25 | 26 | /** 27 | * @since 3.0 28 | * @var LatLongValue 29 | */ 30 | protected $rectangleSouthWest; 31 | 32 | /** 33 | * @since 3.0 34 | * 35 | * @param LatLongValue $rectangleNorthEast 36 | * @param LatLongValue $rectangleSouthWest 37 | * 38 | * @throws InvalidArgumentException 39 | */ 40 | public function __construct( LatLongValue $rectangleNorthEast, LatLongValue $rectangleSouthWest ) { 41 | if ( $rectangleNorthEast->equals( $rectangleSouthWest ) ) { 42 | throw new InvalidArgumentException( '$rectangleNorthEast cannot be equal to $rectangleSouthWest' ); 43 | } 44 | 45 | // TODO: validate bounds are correct, if not, flip 46 | $this->setRectangleNorthEast( $rectangleNorthEast ); 47 | $this->setRectangleSouthWest( $rectangleSouthWest ); 48 | } 49 | 50 | public function getJSONObject( string $defText = '', string $defTitle = '' ): array { 51 | $parentArray = parent::getJSONObject( $defText, $defTitle ); 52 | $array = [ 53 | 'ne' => [ 54 | 'lon' => $this->getRectangleNorthEast()->getLongitude(), 55 | 'lat' => $this->getRectangleNorthEast()->getLatitude() 56 | ], 57 | 'sw' => [ 58 | 'lon' => $this->getRectangleSouthWest()->getLongitude(), 59 | 'lat' => $this->getRectangleSouthWest()->getLatitude() 60 | ], 61 | ]; 62 | 63 | return array_merge( $parentArray, $array ); 64 | } 65 | 66 | public function getRectangleNorthEast(): LatLongValue { 67 | return $this->rectangleNorthEast; 68 | } 69 | 70 | public function setRectangleNorthEast( LatLongValue $rectangleNorthEast ) { 71 | $this->rectangleNorthEast = $rectangleNorthEast; 72 | } 73 | 74 | public function getRectangleSouthWest(): LatLongValue { 75 | return $this->rectangleSouthWest; 76 | } 77 | 78 | public function setRectangleSouthWest( LatLongValue $rectangleSouthWest ) { 79 | $this->rectangleSouthWest = $rectangleSouthWest; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/LegacyModel/WmsOverlay.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class WmsOverlay extends BaseElement { 16 | 17 | /** 18 | * @var String Base url to WMS server 19 | */ 20 | private $wmsServerUrl; 21 | 22 | /** 23 | * @var String WMS Layer name 24 | */ 25 | private $wmsLayerName; 26 | 27 | /** 28 | * @var String WMS Style name (default value: 'default') 29 | */ 30 | private $wmsStyleName; 31 | 32 | public function __construct( string $wmsServerUrl, string $wmsLayerName, string $wmsStyleName = "default" ) { 33 | $this->setWmsServerUrl( $wmsServerUrl ); 34 | $this->setWmsLayerName( $wmsLayerName ); 35 | $this->setWmsStyleName( $wmsStyleName ); 36 | } 37 | 38 | public function getJSONObject( string $defText = '', string $defTitle = '' ): array { 39 | $parentArray = parent::getJSONObject( $defText, $defTitle ); 40 | 41 | $array = [ 42 | 'wmsServerUrl' => $this->getWmsServerUrl(), 43 | 'wmsLayerName' => $this->getWmsLayerName(), 44 | 'wmsStyleName' => $this->getWmsStyleName() 45 | ]; 46 | return array_merge( $parentArray, $array ); 47 | } 48 | 49 | public function getWmsServerUrl(): string { 50 | return $this->wmsServerUrl; 51 | } 52 | 53 | public function setWmsServerUrl( string $wmsServerUrl ) { 54 | $this->wmsServerUrl = $wmsServerUrl; 55 | } 56 | 57 | public function getWmsLayerName(): string { 58 | return $this->wmsLayerName; 59 | } 60 | 61 | public function setWmsLayerName( string $wmsLayerName ) { 62 | $this->wmsLayerName = $wmsLayerName; 63 | } 64 | 65 | public function getWmsStyleName(): string { 66 | return $this->wmsStyleName; 67 | } 68 | 69 | public function setWmsStyleName( string $wmsStyleName ) { 70 | $this->wmsStyleName = $wmsStyleName; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/Map/CargoFormat/CargoFormat.php: -------------------------------------------------------------------------------- 1 | parser = $parser; 22 | } 23 | 24 | public static function allowedParameters() { 25 | return [ 26 | 'height' => [ 'type' => 'int', 'label' => wfMessage( 'cargo-viewdata-heightparam' )->parse() ], 27 | 'width' => [ 'type' => 'int', 'label' => wfMessage( 'cargo-viewdata-widthparam' )->parse() ], 28 | 'icon' => [ 'type' => 'string' ], 29 | 'zoom' => [ 'type' => 'int' ] 30 | ]; 31 | } 32 | 33 | public function display( array $valuesTable, array $formattedValuesTable, array $fieldDescriptions, array $displayParams ) { 34 | $mapOutput = $this->getResultBuilder()->buildOutputFromCargoData( 35 | $valuesTable, 36 | $formattedValuesTable, 37 | $fieldDescriptions, 38 | $displayParams 39 | ); 40 | 41 | if ( $this->parser !== null ) { 42 | $this->getParserOutput()->addHeadItem( $mapOutput->getHeadItems() ); 43 | $this->getParserOutput()->addModules( $mapOutput->getResourceModules() ); 44 | } 45 | 46 | return $mapOutput->getHtml(); 47 | } 48 | 49 | private function getParserOutput(): ParserOutput { 50 | if ( $this->parser instanceof Parser ) { 51 | return $this->parser->getOutput(); 52 | } 53 | 54 | return $this->parser; 55 | } 56 | 57 | private function getResultBuilder(): CargoOutputBuilder { 58 | return MapsFactory::globalInstance()->newCargoOutputBuilder(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/Map/MapData.php: -------------------------------------------------------------------------------- 1 | parameters = $parameters; 18 | } 19 | 20 | public function getParameters(): array { 21 | return $this->parameters; 22 | } 23 | 24 | public function setParameters( array $parameters ): void { 25 | $this->parameters = $parameters; 26 | } 27 | 28 | /** 29 | * @return Marker[] 30 | */ 31 | public function getMarkers(): array { 32 | return $this->markers; 33 | } 34 | 35 | /** 36 | * @param Marker[] $markers 37 | */ 38 | public function setMarkers( array $markers ): void { 39 | $this->markers = $markers; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Map/MapDataSerializer.php: -------------------------------------------------------------------------------- 1 | getParameters(); 11 | 12 | $json['locations'] = array_merge( 13 | array_key_exists( 'locations', $json ) ? $json['locations'] : [], 14 | $this->getLocationJson( $mapData ) 15 | ); 16 | 17 | return $json; 18 | } 19 | 20 | private function getLocationJson( MapData $mapData ): array { 21 | return array_map( 22 | function( Marker $marker ) { 23 | return [ 24 | 'lat' => $marker->getCoordinates()->getLatitude(), 25 | 'lon' => $marker->getCoordinates()->getLongitude(), 26 | 'icon' => $marker->getIconUrl(), 27 | 'text' => $marker->getText(), 28 | ]; 29 | }, 30 | $mapData->getMarkers() 31 | ); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/Map/MapHtmlBuilder.php: -------------------------------------------------------------------------------- 1 | $mapId, 25 | 'style' => "width: {$json['width']}; height: {$json['height']}; background-color: #eeeeee; overflow: hidden;", 26 | 'class' => 'maps-map maps-' . $serviceName, 27 | 'data-mw-maps-mapdata' => FormatJson::encode( $json ) 28 | ], 29 | Html::element( 30 | 'div', 31 | [ 32 | 'class' => 'maps-loading-message' 33 | ], 34 | wfMessage( 'maps-loading-map' )->inContentLanguage()->text() 35 | ) 36 | ); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/Map/MapOutput.php: -------------------------------------------------------------------------------- 1 | html = $html; 15 | $this->resourceModules = $resourceModules; 16 | $this->headItems = $headItems; 17 | } 18 | 19 | public function getHtml(): string { 20 | return $this->html; 21 | } 22 | 23 | public function getHeadItems(): string { 24 | return $this->headItems; 25 | } 26 | 27 | public function getResourceModules(): array { 28 | return $this->resourceModules; 29 | } 30 | 31 | public function addResourcesToParserOutput( \ParserOutput $po ): void { 32 | if ( $this->headItems !== '' ) { 33 | $po->addHeadItem( $this->headItems ); 34 | } 35 | 36 | $po->addModules( $this->resourceModules ); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/Map/MapOutputBuilder.php: -------------------------------------------------------------------------------- 1 | buildMapHtml( $service, $mapData ), 14 | $service->getResourceModules( $mapData->getParameters() ), 15 | $service->getDependencyHtml( $mapData->getParameters() ) 16 | ); 17 | } 18 | 19 | private function buildMapHtml( MappingService $service, MapData $mapData ): string { 20 | $htmlBuilder = new MapHtmlBuilder(); 21 | 22 | return $htmlBuilder->getMapHTML( 23 | ( new MapDataSerializer() )->toJson( $mapData ), 24 | $service->newMapId(), 25 | $service->getName() 26 | ); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/Map/Marker.php: -------------------------------------------------------------------------------- 1 | coordinates = $coordinates; 17 | } 18 | 19 | public function getCoordinates(): LatLongValue { 20 | return $this->coordinates; 21 | } 22 | 23 | public function getIconUrl(): string { 24 | return $this->iconUrl; 25 | } 26 | 27 | public function setIconUrl( string $iconUrl ): void { 28 | $this->iconUrl = $iconUrl; 29 | } 30 | 31 | public function getText(): string { 32 | return $this->text; 33 | } 34 | 35 | public function setText( string $text ): void { 36 | $this->text = $text; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/Map/StructuredPopup.php: -------------------------------------------------------------------------------- 1 | titleHtml = $titleHtml; 18 | $this->propertyValues = $propertyValues; 19 | } 20 | 21 | public function getHtml(): string { 22 | $valueList = $this->getPropertyValueList(); 23 | $separator = $this->titleHtml === '' || $valueList === '' ? '' : '
'; 24 | 25 | return '

' . $this->titleHtml . '

' . $separator . $valueList; 26 | } 27 | 28 | private function getPropertyValueList(): string { 29 | $lines = []; 30 | 31 | foreach ( $this->propertyValues as $name => $value ) { 32 | $lines[] = $this->bold( $this->stripTags( $name ) ) . ': ' . $this->stripTags( $value ); 33 | } 34 | 35 | return implode( '
', $lines ); 36 | } 37 | 38 | private function stripTags( string $html ): string { 39 | return strip_tags( $html, '' ); 40 | } 41 | 42 | private function bold( string $html ): string { 43 | return '' . $html . ''; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/MappingService.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | interface MappingService { 16 | 17 | public function getName(): string; 18 | 19 | public function getAliases(): array; 20 | 21 | /** 22 | * @return array[]|ParamDefinition[] 23 | */ 24 | public function getParameterInfo(): array; 25 | 26 | public function getDependencyHtml( array $params ): string; 27 | 28 | /** 29 | * Returns the resource modules that need to be loaded to use this mapping service. 30 | */ 31 | public function getResourceModules( array $params ): array; 32 | 33 | public function newMapId(): string; 34 | 35 | public function newMapDataFromProcessingResult( ProcessingResult $processingResult ): MapData; 36 | 37 | public function newMapDataFromParameters( array $params ): MapData; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/MapsRegistration.php: -------------------------------------------------------------------------------- 1 | setup(); 30 | 31 | return true; 32 | }; 33 | 34 | return true; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/ParserHooks/CoordinatesFunction.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class CoordinatesFunction implements HookHandler { 21 | 22 | public function handle( Parser $parser, ProcessingResult $result ) { 23 | foreach ( $result->getErrors() as $error ) { 24 | if ( $error->isFatal() ) { 25 | return '
' . 26 | wfMessage( 'maps-validator-fatal-error', $error->getMessage() )->parse() . 27 | '


'; 28 | } 29 | } 30 | 31 | $parameters = $result->getParameters(); 32 | 33 | return MapsFactory::globalInstance()->getCoordinateFormatter()->format( 34 | $parameters['location']->getValue(), 35 | $parameters['format']->getValue(), 36 | $parameters['directional']->getValue() 37 | ); 38 | } 39 | 40 | public static function getHookDefinition(): HookDefinition { 41 | return new HookDefinition( 42 | 'coordinates', 43 | self::getParameterInfo(), 44 | [ 'location', 'format', 'directional' ] 45 | ); 46 | } 47 | 48 | private static function getParameterInfo(): array { 49 | global $egMapsAvailableCoordNotations; 50 | global $egMapsCoordinateNotation; 51 | global $egMapsCoordinateDirectional; 52 | 53 | $params = []; 54 | 55 | $params['location'] = [ 56 | 'type' => 'coordinate', 57 | ]; 58 | 59 | $params['format'] = [ 60 | 'default' => $egMapsCoordinateNotation, 61 | 'values' => $egMapsAvailableCoordNotations, 62 | 'aliases' => 'notation', 63 | 'tolower' => true, 64 | ]; 65 | 66 | $params['directional'] = [ 67 | 'type' => 'boolean', 68 | 'default' => $egMapsCoordinateDirectional, 69 | ]; 70 | 71 | // Give grep a chance to find the usages: 72 | // maps-coordinates-par-location, maps-coordinates-par-format, maps-coordinates-par-directional 73 | foreach ( $params as $name => &$param ) { 74 | $param['message'] = 'maps-coordinates-par-' . $name; 75 | } 76 | 77 | return $params; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/ParserHooks/DistanceFunction.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class DistanceFunction implements HookHandler { 21 | 22 | public function handle( Parser $parser, ProcessingResult $result ) { 23 | foreach ( $result->getErrors() as $error ) { 24 | if ( $error->isFatal() ) { 25 | return '
' . 26 | wfMessage( 'maps-validator-fatal-error', $error->getMessage() )->parse() . 27 | '


'; 28 | } 29 | } 30 | 31 | $parameters = $result->getParameters(); 32 | 33 | return MapsDistanceParser::formatDistance( 34 | $parameters['distance']->getValue(), 35 | $parameters['unit']->getValue(), 36 | $parameters['decimals']->getValue() 37 | ); 38 | } 39 | 40 | public static function getHookDefinition(): HookDefinition { 41 | return new HookDefinition( 42 | 'distance', 43 | self::getParameterInfo(), 44 | [ 'distance', 'unit', 'decimals' ] 45 | ); 46 | } 47 | 48 | private static function getParameterInfo(): array { 49 | global $egMapsDistanceUnit, $egMapsDistanceDecimals; 50 | 51 | $params = []; 52 | 53 | $params['distance'] = [ 54 | 'type' => 'distance', 55 | ]; 56 | 57 | $params['unit'] = [ 58 | 'default' => $egMapsDistanceUnit, 59 | 'values' => MapsDistanceParser::getUnits(), 60 | ]; 61 | 62 | $params['decimals'] = [ 63 | 'type' => 'integer', 64 | 'default' => $egMapsDistanceDecimals, 65 | ]; 66 | 67 | // Give grep a chance to find the usages: 68 | // maps-distance-par-distance, maps-distance-par-unit, maps-distance-par-decimals 69 | foreach ( $params as $name => &$param ) { 70 | $param['message'] = 'maps-distance-par-' . $name; 71 | } 72 | 73 | return $params; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/Presentation/CoordinateFormatter.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class CoordinateFormatter { 16 | 17 | private const PRECISION_MAP = [ 18 | 'dms' => 1 / 360000, 19 | 'dm' => 1 / 600000, 20 | 'dd' => 1 / 1000000, 21 | 'float' => 1 / 1000000, 22 | ]; 23 | 24 | public function format( LatLongValue $latLong, string $format, bool $directional ): string { 25 | $formatter = new LatLongFormatter( new FormatterOptions( 26 | [ 27 | LatLongFormatter::OPT_FORMAT => $format, 28 | LatLongFormatter::OPT_DIRECTIONAL => $directional, 29 | LatLongFormatter::OPT_PRECISION => self::PRECISION_MAP[$format] 30 | ] 31 | ) ); 32 | 33 | return $formatter->format( $latLong ); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/Presentation/ElementJsonSerializer.php: -------------------------------------------------------------------------------- 1 | parser = $parser; 15 | } 16 | 17 | public function elementToJson( BaseElement $element ): array { 18 | $json = $element->getArrayValue(); 19 | 20 | $this->titleAndText( $json ); 21 | 22 | return $json; 23 | } 24 | 25 | public function titleAndText( array &$elementJson ): void { 26 | $elementJson['title'] = $this->parser->wikitextToHtml( $elementJson['title'] ); 27 | $elementJson['text'] = $this->parser->wikitextToHtml( $elementJson['text'] ); 28 | 29 | $hasTitleAndText = $elementJson['title'] !== '' && $elementJson['text'] !== ''; 30 | $elementJson['text'] = ( $hasTitleAndText ? '' . $elementJson['title'] . '' : $elementJson['title'] ) . $elementJson['text']; 31 | $elementJson['title'] = strip_tags( $elementJson['title'] ); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/Presentation/KmlFormatter.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class KmlFormatter { 14 | 15 | /** 16 | * Builds and returns KML representing the set geographical objects. 17 | */ 18 | public function formatLocationsAsKml( Location ...$locations ): string { 19 | $elements = $this->getKmlForLocations( $locations ); 20 | 21 | // http://earth.google.com/kml/2.2 22 | return << 24 | 25 | 26 | $elements 27 | 28 | 29 | EOT; 30 | } 31 | 32 | private function getKmlForLocations( array $locations ): string { 33 | return implode( 34 | "\n", 35 | array_map( 36 | function( Location $location ) { 37 | return $this->locationToKmlPlacemark( $location ); 38 | }, 39 | $locations 40 | ) 41 | ); 42 | } 43 | 44 | private function locationToKmlPlacemark( Location $location ): string { 45 | // TODO: escaping? 46 | $name = 'getTitle() . ']]>'; 47 | 48 | // TODO: escaping? 49 | $description = 'getText() . ']]>'; 50 | 51 | $coordinates = '' 52 | . $this->escapeValue( $this->getCoordinateString( $location ) ) 53 | . ''; 54 | 55 | return << 57 | $name 58 | $description 59 | 60 | $coordinates 61 | 62 | 63 | EOT; 64 | } 65 | 66 | private function getCoordinateString( Location $location ): string { 67 | // lon,lat[,alt] 68 | return $location->getCoordinates()->getLongitude() 69 | . ',' . $location->getCoordinates()->getLatitude() 70 | . ',0'; 71 | } 72 | 73 | private function escapeValue( string $value ): string { 74 | return htmlspecialchars( $value, ENT_NOQUOTES ); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/Presentation/OutputFacade.php: -------------------------------------------------------------------------------- 1 | outputPage = $outputPage; 18 | return $instance; 19 | } 20 | 21 | public static function newFromParserOutput( ParserOutput $parserOutput ) { 22 | $instance = new self(); 23 | $instance->parserOutput = $parserOutput; 24 | return $instance; 25 | } 26 | 27 | public function addHtml( string $html ) { 28 | if ( $this->outputPage !== null ) { 29 | $this->outputPage->addHTML( $html ); 30 | } 31 | 32 | if ( $this->parserOutput !== null ) { 33 | // Append the HTML to any existing text. 34 | $existingText = $this->parserOutput->hasText() ? $this->parserOutput->getRawText() : ''; 35 | $this->parserOutput->setText( $existingText . $html ); 36 | } 37 | } 38 | 39 | public function addModules( string ...$modules ) { 40 | if ( $this->outputPage !== null ) { 41 | $this->outputPage->addModules( $modules ); 42 | } 43 | 44 | if ( $this->parserOutput !== null ) { 45 | $this->parserOutput->addModules( $modules ); 46 | } 47 | } 48 | 49 | public function addHeadItem( string $name, string $html ) { 50 | if ( $this->outputPage !== null ) { 51 | $this->outputPage->addHeadItem( $name, $html ); 52 | } 53 | 54 | if ( $this->parserOutput !== null ) { 55 | $this->parserOutput->addHeadItem( $html, $name ); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/Presentation/ParameterExtractor.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class ParameterExtractor { 12 | 13 | /** 14 | * Extracts the value of a parameter from a parameter list. 15 | * 16 | * @param string[] $parameterNames Name and aliases of the parameter. First match gets used 17 | * @param string[] $rawParameters Parameters that did not get processed further than being put in a key-value map 18 | */ 19 | public function extract( array $parameterNames, array $rawParameters ): ?string { 20 | foreach ( $parameterNames as $parameterName ) { 21 | foreach ( $rawParameters as $rawName => $rawValue ) { 22 | if ( trim( strtolower( $rawName ) ) === $parameterName ) { 23 | return trim( $rawValue ); 24 | } 25 | } 26 | } 27 | 28 | return null; 29 | } 30 | 31 | public static function extractFromKeyValueStrings( array $keyValueStrings ): array { 32 | $rawParameters = []; 33 | 34 | foreach ( $keyValueStrings as $keyValueString ) { 35 | $parts = explode( '=', $keyValueString, 2 ); 36 | 37 | if ( count( $parts ) === 2 ) { 38 | $rawParameters[$parts[0]] = $parts[1]; 39 | } 40 | } 41 | 42 | return $rawParameters; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/Presentation/WikitextParser.php: -------------------------------------------------------------------------------- 1 | parser = $parser; 16 | } 17 | 18 | public function wikitextToHtml( string $text ): string { 19 | if ( trim( $text ) === '' ) { 20 | return ''; 21 | } 22 | 23 | return $this->parser->parse( 24 | $text, 25 | $this->parser->getTitle(), 26 | new ParserOptions( method_exists( $this->parser, 'getUserIdentity' ) ? $this->parser->getUserIdentity() : $this->parser->getUser() ) 27 | )->getText(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/SemanticMW/CoordinateDescription.php: -------------------------------------------------------------------------------- 1 | newDataValueByItem( 21 | $this->getDataItem(), 22 | $this->getProperty() 23 | )->getWikiValue(); 24 | 25 | return $asValue ? $queryString : "[[$queryString]]"; 26 | } 27 | 28 | /** 29 | * @see SomePropertyInterpreter::mapValueDescription 30 | * 31 | * FIXME: store specific code should be in the store component 32 | * 33 | * @param string $tableName 34 | * @param string[] $fieldNames 35 | * @param IDatabase $db 36 | * 37 | * @return string|false 38 | */ 39 | public function getSQLCondition( $tableName, array $fieldNames, IDatabase $db ) { 40 | $dataItem = $this->getDataItem(); 41 | 42 | // Only execute the query when the description's type is geographical coordinates, 43 | // the description is valid, and the near comparator is used. 44 | if ( $dataItem instanceof SMWDIGeoCoord ) { 45 | switch ( $this->getComparator() ) { 46 | case SMW_CMP_EQ: 47 | $comparator = '='; 48 | break; 49 | case SMW_CMP_LEQ: 50 | $comparator = '<='; 51 | break; 52 | case SMW_CMP_GEQ: 53 | $comparator = '>='; 54 | break; 55 | case SMW_CMP_NEQ: 56 | $comparator = '!='; 57 | break; 58 | default: 59 | return false; 60 | } 61 | 62 | $lat = $db->addQuotes( $dataItem->getLatitude() ); 63 | $lon = $db->addQuotes( $dataItem->getLongitude() ); 64 | 65 | $conditions = []; 66 | 67 | $conditions[] = "{$tableName}.$fieldNames[1] $comparator $lat"; 68 | $conditions[] = "{$tableName}.$fieldNames[2] $comparator $lon"; 69 | 70 | return implode( ' AND ', $conditions ); 71 | } 72 | 73 | return false; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/SemanticMW/TemplatedPopup.php: -------------------------------------------------------------------------------- 1 | templateName = $templateName; 16 | $this->extraParameter = $extraParameter; 17 | } 18 | 19 | public function getWikiText( string $fullTitle, $displayTitle, LatLongValue $latLong, array $properties ): string { 20 | $segments = [ 21 | $this->templateName, 22 | 'fulltitle=' . $fullTitle, 23 | 'title=' . $displayTitle, 24 | 'latitude=' . $latLong->getLatitude(), 25 | 'longitude=' . $latLong->getLongitude(), 26 | 'userparam=' . $this->extraParameter 27 | ]; 28 | 29 | foreach ( $properties as $name => $value ) { 30 | $segments[] = $name . '=' . $value; 31 | $segments[] = $value; 32 | } 33 | 34 | return '{{' . implode( '|', $segments ) . '}}'; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/SemanticMapsSetup.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class SemanticMapsSetup { 18 | 19 | private MappingServices $mappingServices; 20 | 21 | public function __construct( MappingServices $mappingServices ) { 22 | $this->mappingServices = $mappingServices; 23 | } 24 | 25 | public function initExtension() { 26 | // Hook for initializing the Geographical Data types. 27 | $hooks = []; 28 | $hooks['SMW::DataType::initTypes'][] = function() { 29 | DataTypeRegistry::getInstance()->registerDatatype( 30 | '_geo', 31 | CoordinateValue::class, 32 | SMWDataItem::TYPE_GEO 33 | ); 34 | 35 | return true; 36 | }; 37 | 38 | // Hook for defining the default query printer for queries that ask for geographical coordinates. 39 | $hooks['SMWResultFormat'][] = 'Maps\MapsHooks::addGeoCoordsDefaultFormat'; 40 | MapsHooks::registerHookHandlers( $hooks ); 41 | 42 | $this->registerGoogleMaps(); 43 | $this->registerLeaflet(); 44 | 45 | $GLOBALS['smwgResultFormats']['kml'] = KmlPrinter::class; 46 | 47 | $GLOBALS['smwgResultAliases'][$GLOBALS['egMapsDefaultService']][] = 'map'; 48 | MapPrinter::registerDefaultService( $GLOBALS['egMapsDefaultService'] ); 49 | } 50 | 51 | private function registerGoogleMaps() { 52 | if ( $this->mappingServices->nameIsKnown( 'googlemaps3' ) ) { 53 | $googleMaps = $this->mappingServices->getService( 'googlemaps3' ); 54 | 55 | MapPrinter::registerService( $googleMaps ); 56 | 57 | $GLOBALS['smwgResultFormats'][$googleMaps->getName()] = MapPrinter::class; 58 | $GLOBALS['smwgResultAliases'][$googleMaps->getName()] = $googleMaps->getAliases(); 59 | } 60 | } 61 | 62 | private function registerLeaflet() { 63 | if ( $this->mappingServices->nameIsKnown( 'leaflet' ) ) { 64 | $leaflet = $this->mappingServices->getService( 'leaflet' ); 65 | 66 | MapPrinter::registerService( $leaflet ); 67 | 68 | $GLOBALS['smwgResultFormats'][$leaflet->getName()] = MapPrinter::class; 69 | $GLOBALS['smwgResultAliases'][$leaflet->getName()] = $leaflet->getAliases(); 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/WikitextParsers/CircleParser.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class CircleParser implements ValueParser { 23 | 24 | private string $metaDataSeparator = '~'; 25 | private Geocoder $geocoder; 26 | 27 | public function __construct( $geocoder = null ) { 28 | $this->geocoder = $geocoder instanceof Geocoder ? $geocoder : MapsFactory::globalInstance()->getGeocoder(); 29 | } 30 | 31 | /** 32 | * @see StringValueParser::stringParse 33 | * 34 | * @since 3.0 35 | * 36 | * @param string $value 37 | */ 38 | public function parse( $value ): Circle { 39 | $metaData = explode( $this->metaDataSeparator, $value ); 40 | $circleData = explode( ':', array_shift( $metaData ) ); 41 | 42 | $circle = new Circle( $this->stringToLatLongValue( $circleData[0] ), (float)$circleData[1] ); 43 | 44 | if ( $metaData !== [] ) { 45 | $circle->setTitle( array_shift( $metaData ) ); 46 | } 47 | 48 | if ( $metaData !== [] ) { 49 | $circle->setText( array_shift( $metaData ) ); 50 | } 51 | 52 | if ( $metaData !== [] ) { 53 | $circle->setStrokeColor( array_shift( $metaData ) ); 54 | } 55 | 56 | if ( $metaData !== [] ) { 57 | $circle->setStrokeOpacity( array_shift( $metaData ) ); 58 | } 59 | 60 | if ( $metaData !== [] ) { 61 | $circle->setStrokeWeight( array_shift( $metaData ) ); 62 | } 63 | 64 | if ( $metaData !== [] ) { 65 | $circle->setFillColor( array_shift( $metaData ) ); 66 | } 67 | 68 | if ( $metaData !== [] ) { 69 | $circle->setFillOpacity( array_shift( $metaData ) ); 70 | } 71 | 72 | return $circle; 73 | } 74 | 75 | private function stringToLatLongValue( string $location ): LatLongValue { 76 | $latLong = $this->geocoder->geocode( $location ); 77 | 78 | if ( $latLong === null ) { 79 | throw new ParseException( 'Failed to parse or geocode' ); 80 | } 81 | 82 | return $latLong; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/WikitextParsers/DistanceParser.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class DistanceParser extends StringValueParser { 19 | 20 | /** 21 | * @see StringValueParser::stringParse 22 | * 23 | * @since 3.0 24 | * 25 | * @param string $value 26 | * 27 | * @throws ParseException 28 | */ 29 | public function stringParse( $value ): float { 30 | $distance = \Maps\Presentation\MapsDistanceParser::parseDistance( $value ); 31 | 32 | if ( is_float( $distance ) ) { 33 | return $distance; 34 | } 35 | 36 | throw new ParseException( 'Not a distance' ); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/WikitextParsers/PolygonParser.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class PolygonParser extends LineParser { 19 | 20 | protected function constructShapeFromLatLongValues( array $locations ): Line { 21 | return new Polygon( $locations ); 22 | } 23 | 24 | protected function handleCommonParams( array &$params, Line &$line ) { 25 | parent::handleCommonParams( $params, $line ); 26 | $this->handlePolygonParams( $params, $line ); 27 | } 28 | 29 | protected function handlePolygonParams( array &$params, Polygon &$polygon ) { 30 | if ( $fillColor = array_shift( $params ) ) { 31 | $polygon->setFillColor( $fillColor ); 32 | } 33 | 34 | if ( $fillOpacity = array_shift( $params ) ) { 35 | $polygon->setFillOpacity( $fillOpacity ); 36 | } 37 | 38 | if ( $showOnlyOnHover = array_shift( $params ) ) { 39 | $polygon->setOnlyVisibleOnHover( strtolower( trim( $showOnlyOnHover ) ) === 'on' ); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/WikitextParsers/RectangleParser.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | class RectangleParser implements ValueParser { 23 | 24 | private string $metaDataSeparator = '~'; 25 | private Geocoder $geocoder; 26 | 27 | public function __construct( $geocoder = null ) { 28 | $this->geocoder = $geocoder instanceof Geocoder ? $geocoder : MapsFactory::globalInstance()->getGeocoder(); 29 | } 30 | 31 | /** 32 | * @see StringValueParser::stringParse 33 | * 34 | * @since 3.0 35 | * 36 | * @param string $value 37 | */ 38 | public function parse( $value ): Rectangle { 39 | $metaData = explode( $this->metaDataSeparator, $value ); 40 | $rectangleData = explode( ':', array_shift( $metaData ) ); 41 | 42 | $rectangle = new Rectangle( 43 | $this->stringToLatLongValue( $rectangleData[0] ), 44 | $this->stringToLatLongValue( $rectangleData[1] ) 45 | ); 46 | 47 | if ( $metaData !== [] ) { 48 | $rectangle->setTitle( array_shift( $metaData ) ); 49 | } 50 | 51 | if ( $metaData !== [] ) { 52 | $rectangle->setText( array_shift( $metaData ) ); 53 | } 54 | 55 | if ( $metaData !== [] ) { 56 | $rectangle->setStrokeColor( array_shift( $metaData ) ); 57 | } 58 | 59 | if ( $metaData !== [] ) { 60 | $rectangle->setStrokeOpacity( array_shift( $metaData ) ); 61 | } 62 | 63 | if ( $metaData !== [] ) { 64 | $rectangle->setStrokeWeight( array_shift( $metaData ) ); 65 | } 66 | 67 | if ( $metaData !== [] ) { 68 | $rectangle->setFillColor( array_shift( $metaData ) ); 69 | } 70 | 71 | if ( $metaData !== [] ) { 72 | $rectangle->setFillOpacity( array_shift( $metaData ) ); 73 | } 74 | 75 | return $rectangle; 76 | } 77 | 78 | private function stringToLatLongValue( string $location ): LatLongValue { 79 | $latLong = $this->geocoder->geocode( $location ); 80 | 81 | if ( $latLong === null ) { 82 | throw new ParseException( 'Failed to parse or geocode' ); 83 | } 84 | 85 | return $latLong; 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/WikitextParsers/WmsOverlayParser.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class WmsOverlayParser implements ValueParser { 20 | 21 | /** 22 | * Parses the provided string and returns the result. 23 | * 24 | * @since 3.0 25 | * 26 | * @param string $value 27 | * 28 | * @throws ParseException 29 | */ 30 | public function parse( $value ): WmsOverlay { 31 | if ( !is_string( $value ) ) { 32 | throw new ParseException( 'Not a string' ); 33 | } 34 | 35 | $separator = " "; 36 | $metaData = explode( $separator, $value ); 37 | 38 | if ( count( $metaData ) >= 2 ) { 39 | $wmsOverlay = new WmsOverlay( $metaData[0], $metaData[1] ); 40 | if ( count( $metaData ) == 3 ) { 41 | $wmsOverlay->setWmsStyleName( $metaData[2] ); 42 | } 43 | 44 | return $wmsOverlay; 45 | } 46 | 47 | throw new ParseException( 'Need at least two parameters, url to WMS server and map layer name' ); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /tests/Integration/DataAccess/MediaWikiFileUrlFinderTest.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class MediaWikiFileUrlFinderTest extends TestCase { 18 | 19 | /** 20 | * @var FileUrlFinder 21 | */ 22 | private $urlFinder; 23 | 24 | public function setUp(): void { 25 | $this->urlFinder = new MediaWikiFileUrlFinder(); 26 | } 27 | 28 | public function testGivenUrl_urlIsReturnedAsProvided() { 29 | $this->assertSame( 30 | 'http://example.com/such', 31 | $this->urlFinder->getUrlForFileName( 'http://example.com/such' ) 32 | ); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /tests/Integration/InitializationTest.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class InitializationTest extends TestCase { 14 | 15 | public function testVersionConstantIsDefined() { 16 | $this->assertIsInt( NS_GEO_JSON ); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /tests/Integration/MapsMapperTest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class MapsMapperTest extends TestCase { 17 | 18 | public function setUp(): void { 19 | if ( !defined( 'MEDIAWIKI' ) ) { 20 | $this->markTestSkipped( 'MediaWiki is not available' ); 21 | } 22 | } 23 | 24 | public function imageUrlProvider() { 25 | return [ 26 | [ 'markerImage.png', 'markerImage.png' ], 27 | [ '/w/images/c/ce/Green_marker.png', '/w/images/c/ce/Green_marker.png' ], 28 | [ 29 | '//semantic-mediawiki.org/w/images/c/ce/Green_marker.png', 30 | '//semantic-mediawiki.org/w/images/c/ce/Green_marker.png' 31 | ], 32 | [ 'Cat2.jpg', 'Cat2.jpg' ], 33 | ]; 34 | } 35 | 36 | /** 37 | * Tests MapsMapperTest::getFileUrl() 38 | * 39 | * @dataProvider imageUrlProvider 40 | */ 41 | public function testGetFileUrl( $file, $expected ) { 42 | $this->assertSame( $expected, MapsFunctions::getFileUrl( $file ) ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Integration/MapsSetupTest.php: -------------------------------------------------------------------------------- 1 | assertTrue( class_exists( $GLOBALS['wgSpecialPages']['MapEditor'] ) ); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tests/Integration/Parser/DistanceTest.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class DistanceTest extends TestCase { 15 | 16 | private function parse( string $textToParse ): string { 17 | return TestFactory::newInstance()->parse( $textToParse ); 18 | } 19 | 20 | public function testFormatting(): void { 21 | $this->assertStringContainsString( 22 | '42,000 m', 23 | $this->parse( '{{#distance:42km}}' ) 24 | ); 25 | } 26 | 27 | public function testDecimals(): void { 28 | $this->assertStringContainsString( 29 | '42,123.45 m', 30 | $this->parse( '{{#distance:42.12345km|decimals=2}}' ) 31 | ); 32 | } 33 | 34 | public function testUnit(): void { 35 | $this->assertStringContainsString( 36 | '26.1 miles', 37 | $this->parse( '{{#distance:42km|unit=miles}}' ) 38 | ); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /tests/Integration/Parser/FindDestinationTest.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class FindDestinationTest extends TestCase { 15 | 16 | private function parse( string $textToParse ): string { 17 | return TestFactory::newInstance()->parse( $textToParse ); 18 | } 19 | 20 | public function testBaseCase(): void { 21 | $this->assertStringContainsString( 22 | '55° 45\' 20.83" N, 37° 36\' 34.71" W', 23 | $this->parse( '{{#finddestination:55.7557860° N, 37.6176330° W|90|500}}' ) 24 | ); 25 | } 26 | 27 | public function testParameters(): void { 28 | $this->assertStringContainsString( 29 | '40.712728 N, 74.000083 W', 30 | $this->parse( '{{#finddestination:New York|bearing=90|distance=500|format=float}}' ) 31 | ); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tests/Integration/Parser/GeoDistanceTest.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class GeoDistanceTest extends TestCase { 15 | 16 | private function parse( string $textToParse ): string { 17 | return TestFactory::newInstance()->parse( $textToParse ); 18 | } 19 | 20 | public function testParameters(): void { 21 | $this->assertStringContainsString( 22 | '6,385 km', 23 | $this->parse( '{{#geodistance:52.5200° N, 13.4050° E|40.7128° N, 74.0060° W|unit=km}}' ) 24 | ); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /tests/Integration/Parser/GoogleMapsTest.php: -------------------------------------------------------------------------------- 1 | assertStringContainsString( htmlspecialchars( $expected ), $html ); 14 | } 15 | 16 | private function parse( string $textToParse ): string { 17 | return TestFactory::newInstance()->parse( $textToParse ); 18 | } 19 | 20 | public function testGoogleMapsKmlFiltersInvalidFileNames() { 21 | $this->assertStringContainsData( 22 | '"kml":["ValidFile.kml"],', 23 | $this->parse( 24 | "{{#google_maps:kml=, ,ValidFile.kml ,}}" 25 | ) 26 | ); 27 | } 28 | 29 | public function testWhenValidZoomIsSpecified_itGetsUsed() { 30 | $this->assertStringContainsData( 31 | '"zoom":5', 32 | $this->parse( '{{#google_maps:1,1|zoom=5}}' ) 33 | ); 34 | } 35 | 36 | public function testWhenZoomIsNotSpecifiedAndThereIsOnlyOneLocation_itIsDefaulted() { 37 | $this->assertStringContainsData( 38 | '"zoom":' . $GLOBALS['egMapsGMaps3Zoom'], 39 | $this->parse( '{{#google_maps:1,1}}' ) 40 | ); 41 | } 42 | 43 | public function testWhenZoomIsNotSpecifiedAndThereAreMultipleLocations_itIsDefaulted() { 44 | $this->assertStringContainsData( 45 | '"zoom":false', 46 | $this->parse( '{{#google_maps:1,1;2,2}}' ) 47 | ); 48 | } 49 | 50 | public function testWhenZoomIsInvalid_itIsDefaulted() { 51 | $this->assertStringContainsData( 52 | '"zoom":' . $GLOBALS['egMapsGMaps3Zoom'], 53 | $this->parse( '{{#google_maps:1,1|zoom=tomato}}' ) 54 | ); 55 | } 56 | 57 | public function testInvalidMapTypesGetLeftOut() { 58 | $this->assertStringContainsData( 59 | '"types":["ROADMAP"]', 60 | $this->parse( '{{#google_maps:1,1|types=normal, foobar}}' ) 61 | ); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /tests/Integration/Parser/LeafletTest.php: -------------------------------------------------------------------------------- 1 | parse( $textToParse ); 16 | } 17 | 18 | private function assertStringContainsData( string $expected, string $html ): void { 19 | $this->assertStringContainsString( htmlspecialchars( $expected ), $html ); 20 | } 21 | 22 | public function testLeafletImageLayersIgnoresNotFoundImages() { 23 | $this->assertStringContainsData( 24 | '"imageLayers":[]', 25 | $this->parse( 26 | "{{#leaflet:image layers=404.png}}" 27 | ) 28 | ); 29 | } 30 | 31 | public function testLeafletImageLayersIgnoresImageUrls() { 32 | $this->assertStringContainsData( 33 | '"imageLayers":[]', 34 | $this->parse( 35 | "{{#leaflet:image layers=https://user-images.githubusercontent.com/62098559/76514021-3fa9be80-647d-11ea-82ae-715420a5c432.png}}" 36 | ) 37 | ); 38 | } 39 | 40 | public function testLeafletImageLayer() { 41 | $factory = MapsTestFactory::newTestInstance(); 42 | 43 | $factory->imageRepo->addImage( 44 | 'MyImage.png', 45 | new ImageValueObject( '/tmp/example/image.png', 40, 20 ) 46 | ); 47 | 48 | $html = $this->parse( "{{#leaflet:image layers=MyImage.png}}" ); 49 | 50 | $this->assertStringContainsData( '"name":"MyImage.png"', $html ); 51 | $this->assertStringContainsData( '"url":"/tmp/example/image.png"', $html ); 52 | $this->assertStringContainsData( '"width":100', $html ); 53 | $this->assertStringContainsData( '"height":50', $html ); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /tests/Integration/Parser/MapsDocTest.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class MapsDocTest extends TestCase { 15 | 16 | private function parse( string $textToParse ): string { 17 | return TestFactory::newInstance()->parse( $textToParse ); 18 | } 19 | 20 | public function testSmoke(): void { 21 | $parsed = $this->parse( '{{#mapsdoc:leaflet|parameters=all}}' ); 22 | 23 | $this->assertStringContainsString( 24 | 'List of circles', 25 | $parsed 26 | ); 27 | 28 | $this->assertStringContainsString( 29 | 'OpenStreetMap', 30 | $parsed 31 | ); 32 | 33 | $this->assertStringContainsString( 34 | 'The maximum radius that a cluster will cover.', 35 | $parsed 36 | ); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /tests/Integration/Parsers/CircleParserTest.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class CircleParserTest extends TestCase { 20 | 21 | public function testGivenCoordinateAndRadius_parserReturnsCircle() { 22 | $parser = new CircleParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) ); 23 | 24 | $circle = $parser->parse( '57.421,23.90625:32684.605182' ); 25 | 26 | $this->assertInstanceOf( Circle::class, $circle ); 27 | 28 | $expectedLatLong = new LatLongValue( 57.421, 23.90625 ); 29 | $this->assertTrue( $expectedLatLong->equals( $circle->getCircleCentre() ) ); 30 | 31 | $this->assertSame( 32684.605182, $circle->getCircleRadius() ); 32 | } 33 | 34 | public function testGivenTitleAndText_circleHasProvidedMetaData() { 35 | $parser = new CircleParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) ); 36 | 37 | $circle = $parser->parse( '57.421,23.90625:32684.605182~title~text' ); 38 | 39 | $this->assertInstanceOf( Circle::class, $circle ); 40 | 41 | $this->assertSame( 'title', $circle->getTitle() ); 42 | $this->assertSame( 'text', $circle->getText() ); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /tests/Integration/Parsers/DistanceParserTest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class DistanceParserTest extends TestCase { 17 | 18 | /** 19 | * @dataProvider validInputProvider 20 | */ 21 | public function testValidInputs( $input, $expected ) { 22 | $this->assertEqualsWithDelta( 23 | $expected, 24 | ( new DistanceParser() )->parse( $input ), 25 | PHP_FLOAT_EPSILON 26 | ); 27 | } 28 | 29 | public function validInputProvider(): array { 30 | return [ 31 | [ '1', 1.0 ], 32 | [ '1m', 1.0 ], 33 | [ '42 km', 42000.0 ], 34 | [ '4.2 km', 4200.0 ], 35 | [ '4.2 m', 4.2 ], 36 | [ '4.02 m', 4.02 ], 37 | // [ '4.02 km', 4020.0 ], 38 | [ '0.001 km', 1.0 ], 39 | ]; 40 | } 41 | 42 | /** 43 | * @dataProvider invalidInputProvider 44 | */ 45 | public function testGivenInvalidInput_exceptionIsThrown( $input ) { 46 | $parser = new DistanceParser(); 47 | 48 | $this->expectException( ParseException::class ); 49 | $parser->parse( $input ); 50 | } 51 | 52 | public function invalidInputProvider() { 53 | return [ 54 | [ '' ], 55 | [ 'kittens' ], 56 | [ '1 kittens' ], 57 | [ '-1m' ], 58 | [ 'foo m' ], 59 | [ '1m foo' ], 60 | [ 'foo 1m' ], 61 | [ 'm1' ], 62 | [ '4. m' ], 63 | [ '4.2.1 m' ], 64 | ]; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /tests/Integration/Parsers/LineParserTest.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class LineParserTest extends TestCase { 19 | 20 | public function testGivenOneCoordinate_lineWithOneCoordinateIsReturned() { 21 | $parser = $this->newParser(); 22 | 23 | $this->assertEquals( 24 | new Line( [ new LatLongValue( 4, 2 ) ] ), 25 | $parser->parse( '4,2' ) 26 | ); 27 | } 28 | 29 | /** 30 | * @return \Maps\WikitextParsers\LineParser 31 | */ 32 | private function newParser() { 33 | $parser = new LineParser(); 34 | 35 | $parser->setGeocoder( 36 | new InMemoryGeocoder( 37 | [ 38 | '4,2' => new LatLongValue( 4, 2 ), 39 | '2,3' => new LatLongValue( 2, 3 ), 40 | ] 41 | ) 42 | ); 43 | 44 | return $parser; 45 | } 46 | 47 | public function testGivenTwoCoordinates_lineWithBothCoordinateIsReturned() { 48 | $parser = $this->newParser(); 49 | 50 | $this->assertEquals( 51 | new Line( 52 | [ 53 | new LatLongValue( 4, 2 ), 54 | new LatLongValue( 2, 3 ) 55 | ] 56 | ), 57 | $parser->parse( '4,2:2,3' ) 58 | ); 59 | } 60 | 61 | public function testTitleAndTextGetSetWhenPresent() { 62 | $parser = $this->newParser(); 63 | 64 | $expectedLine = new Line( 65 | [ 66 | new LatLongValue( 4, 2 ), 67 | new LatLongValue( 2, 3 ) 68 | ] 69 | ); 70 | $expectedLine->setTitle( 'title' ); 71 | $expectedLine->setText( 'text' ); 72 | 73 | $this->assertEquals( 74 | $expectedLine, 75 | $parser->parse( '4,2:2,3~title~text' ) 76 | ); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /tests/Integration/Parsers/RectangleParserTest.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class RectangleParserTest extends TestCase { 20 | 21 | public function testGivenBoundingBox_parserReturnsRectangle() { 22 | $parser = new RectangleParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) ); 23 | 24 | $rectangle = $parser->parse( '51.8357775,33.83789:46,23.37890625' ); 25 | 26 | $this->assertInstanceOf( Rectangle::class, $rectangle ); 27 | 28 | $expectedNorthEast = new LatLongValue( 51.8357775, 33.83789 ); 29 | $this->assertTrue( $expectedNorthEast->equals( $rectangle->getRectangleNorthEast() ) ); 30 | 31 | $expectedSouthWest = new LatLongValue( 46, 23.37890625 ); 32 | $this->assertTrue( $expectedSouthWest->equals( $rectangle->getRectangleSouthWest() ) ); 33 | } 34 | 35 | public function testGivenTitleAndText_rectangleHasProvidedMetaData() { 36 | $parser = new RectangleParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) ); 37 | 38 | $rectangle = $parser->parse( "51.8357775,33.83789:46,23.37890625~I'm a square~of doom" ); 39 | 40 | $this->assertInstanceOf( Rectangle::class, $rectangle ); 41 | 42 | $this->assertSame( "I'm a square", $rectangle->getTitle() ); 43 | $this->assertSame( 'of doom', $rectangle->getText() ); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /tests/Integration/Parsers/WmsOverlayParserTest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class WmsOverlayParserTest extends TestCase { 17 | 18 | public function testGivenValidInput_parserReturnsOverlayObject() { 19 | $parser = new WmsOverlayParser(); 20 | 21 | $overlay = $parser->parse( 'http://demo.cubewerx.com/demo/cubeserv/cubeserv.cgi? Foundation.GTOPO30' ); 22 | 23 | $this->assertSame( 24 | 'http://demo.cubewerx.com/demo/cubeserv/cubeserv.cgi?', 25 | $overlay->getWmsServerUrl() 26 | ); 27 | 28 | $this->assertSame( 29 | 'Foundation.GTOPO30', 30 | $overlay->getWmsLayerName() 31 | ); 32 | } 33 | 34 | public function testWhenStyleNameIsSpecified_getStyleNameReturnsIt() { 35 | $parser = new WmsOverlayParser(); 36 | 37 | $overlay = $parser->parse( 38 | 'http://maps.imr.no:80/geoserver/wms? vulnerable_areas:Identified_coral_area coral_identified_areas' 39 | ); 40 | 41 | $this->assertSame( 42 | 'coral_identified_areas', 43 | $overlay->getWmsStyleName() 44 | ); 45 | } 46 | 47 | public function testWhenThereAreLessThanTwoSegments_parseExceptionIsThrown() { 48 | $parser = new WmsOverlayParser(); 49 | 50 | $this->expectException( ParseException::class ); 51 | $parser->parse( 'Such' ); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /tests/Integration/ResourceModulesTest.php: -------------------------------------------------------------------------------- 1 | getResourceLoader(); 18 | $module = $resourceLoader->getModule( $moduleName ); 19 | 20 | if ( $module->getDependencies() === [] ) { 21 | $this->assertTrue( true ); 22 | } 23 | else { 24 | foreach ( $module->getDependencies() as $dependency ) { 25 | $this->assertNotNull( 26 | $resourceLoader->getModule( $dependency ), 27 | 'Dependency ' . $dependency . ' should exist' 28 | ); 29 | } 30 | } 31 | } 32 | 33 | public function mapsModuleNameProvider() { 34 | foreach ( $this->getResourceLoader()->getModuleNames() as $name ) { 35 | if ( $this->isMapsModule( $name ) ) { 36 | yield $name => [ $name ]; 37 | } 38 | } 39 | } 40 | 41 | private function isMapsModule( string $name ): bool { 42 | $modulePrefix = 'ext.maps.'; 43 | return substr( $name, 0, strlen( $modulePrefix ) ) === $modulePrefix; 44 | } 45 | 46 | private function getResourceLoader(): ResourceLoader { 47 | $mwServices = MediaWikiServices::getInstance(); 48 | 49 | if ( method_exists( $mwServices, 'getResourceLoader' ) ) { 50 | return $mwServices->getResourceLoader(); 51 | } 52 | 53 | return new ResourceLoader(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /tests/Integration/Semantic/AreaDescriptionTest.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class AreaDescriptionTest extends TestCase { 18 | 19 | public function setUp(): void { 20 | if ( !defined( 'SMW_VERSION' ) ) { 21 | $this->markTestSkipped( 'SMW is not available' ); 22 | } 23 | } 24 | 25 | public function testGetBoundingBox() { 26 | $area = new AreaDescription( 27 | new SMWDIGeoCoord( 0, 5 ), 28 | SMW_CMP_EQ, 29 | '10 km' 30 | ); 31 | 32 | $this->assertEquals( 33 | [ 34 | 'north' => 0.089932160591873, 35 | 'east' => 5.089932160591873, 36 | 'south' => -0.089932160591873, 37 | 'west' => 4.9100678394081 38 | ], 39 | $area->getBoundingBox() 40 | ); 41 | } 42 | 43 | public function testGetSQLCondition() { 44 | $area = new AreaDescription( 45 | new SMWDIGeoCoord( 0, 5 ), 46 | SMW_CMP_EQ, 47 | '10 km' 48 | ); 49 | 50 | $this->assertSame( 51 | 'geo_table.lat_field < \'0.089932160591873\' AND geo_table.lat_field > \'-0.089932160591873\' ' 52 | . 'AND geo_table.long_field < \'5.0899321605919\' AND geo_table.long_field > \'4.9100678394081\'', 53 | $area->getSQLCondition( 'geo_table', [ 'id_field', 'lat_field', 'long_field' ], wfGetDB( DB_PRIMARY ) ) 54 | ); 55 | } 56 | 57 | public function testWhenComparatorIsNotSupported_getSQLConditionReturnsFalse() { 58 | $area = new AreaDescription( 59 | new SMWDIGeoCoord( 0, 5 ), 60 | SMW_CMP_LIKE, 61 | '10 km' 62 | ); 63 | 64 | $this->assertFalse( 65 | $area->getSQLCondition( 'geo_table', [ 'id_field', 'lat_field', 'long_field' ], wfGetDB( DB_PRIMARY ) ) 66 | ); 67 | } 68 | 69 | public function testGetQueryString() { 70 | $area = new AreaDescription( 71 | new SMWDIGeoCoord( 1, 5 ), 72 | SMW_CMP_EQ, 73 | '10 km' 74 | ); 75 | 76 | $this->assertSame( 77 | '[[1° 0\' 0.00" N, 5° 0\' 0.00" E (10 km)]]', 78 | $area->getQueryString() 79 | ); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /tests/MapsTestFactory.php: -------------------------------------------------------------------------------- 1 | imageRepo = new InMemoryImageRepository(); 26 | 27 | return self::$globalInstance; 28 | } 29 | 30 | public function getImageRepository(): ImageRepository { 31 | return $this->imageRepo; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tests/TestDoubles/ImageValueObject.php: -------------------------------------------------------------------------------- 1 | url = $url; 17 | $this->widthInPx = $widthInPx; 18 | $this->heightInPx = $heightInPx; 19 | } 20 | 21 | public function getUrl(): string { 22 | return $this->url; 23 | } 24 | 25 | public function getWidthInPx(): int { 26 | return $this->widthInPx; 27 | } 28 | 29 | public function getHeightInPx(): int { 30 | return $this->heightInPx; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /tests/TestDoubles/InMemoryImageRepository.php: -------------------------------------------------------------------------------- 1 | images[$imageName] ?? null; 16 | } 17 | 18 | public function addImage( string $imageName, Image $image ) { 19 | $this->images[$imageName] = $image; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tests/Unit/DataAccess/SubObjectTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped( 'SMW is not available' ); 23 | } 24 | } 25 | 26 | public function testEmpty() { 27 | $subObject = new SubObject( 'MyName' ); 28 | 29 | $container = $subObject->toContainerSemanticData( $this->newTitleValue() ); 30 | 31 | $this->assertEquals( 32 | new DIWikiPage( self::PAGE_TITLE, self::PAGE_NS, '', 'MyName' ), 33 | $container->getSubject() 34 | ); 35 | 36 | $this->assertSame( [], $container->getErrors() ); 37 | $this->assertSame( [], $container->getProperties() ); 38 | } 39 | 40 | private function newTitleValue(): \TitleValue { 41 | return new \TitleValue( self::PAGE_NS, self::PAGE_TITLE ); 42 | } 43 | 44 | public function testPropertyValuesPairsAreTransformed() { 45 | $subObject = new SubObject( 'MyName' ); 46 | $subObject->addPropertyValuePair( 'SuchNumber', new \SMWDINumber( 42 ) ); 47 | $subObject->addPropertyValuePair( 'SuchNumber', new \SMWDINumber( 23 ) ); 48 | $subObject->addPropertyValuePair( 'SuchBoolean', new \SMWDIBoolean( true ) ); 49 | 50 | $container = $subObject->toContainerSemanticData( $this->newTitleValue() ); 51 | 52 | $this->assertEquals( 53 | [ 54 | 'SuchNumber' => new DIProperty( 'SuchNumber' ), 55 | 'SuchBoolean' => new DIProperty( 'SuchBoolean' ), 56 | ], 57 | $container->getProperties() 58 | ); 59 | 60 | $this->assertEquals( 61 | [ 62 | new \SMWDINumber( 42 ) , 63 | new \SMWDINumber( 23 ) , 64 | ], 65 | $container->getPropertyValues( new DIProperty( 'SuchNumber' ) ) 66 | ); 67 | 68 | $this->assertSame( [], $container->getErrors() ); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /tests/Unit/GeoJsonPages/GeoJsonContentTest.php: -------------------------------------------------------------------------------- 1 | assertFalse( ( new GeoJsonContent( '{}' ) )->isValid() ); 14 | } 15 | 16 | public function testMinimalGeoJsonIsValid() { 17 | $this->assertTrue( ( new GeoJsonContent( 18 | '{"type": "FeatureCollection", "features": []}' 19 | ) )->isValid() ); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tests/Unit/LegacyModel/BaseElementTest.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | abstract class BaseElementTest extends TestCase { 19 | 20 | public function invalidConstructorProvider() { 21 | return []; 22 | } 23 | 24 | /** 25 | * Creates and returns a new instance of the concrete class. 26 | * 27 | * @since 3.0 28 | * 29 | * @return mixed 30 | */ 31 | public function newInstance() { 32 | $reflector = new \ReflectionClass( $this->getClass() ); 33 | $args = func_get_args(); 34 | $instance = $reflector->newInstanceArgs( $args ); 35 | return $instance; 36 | } 37 | 38 | /** 39 | * Returns the name of the concrete class tested by this test. 40 | * 41 | * @since 3.0 42 | * 43 | * @return string 44 | */ 45 | abstract public function getClass(); 46 | 47 | /** 48 | * @since 3.0 49 | * 50 | * @return array [instance, constructor args] 51 | */ 52 | public function instanceProvider() { 53 | $phpFails = [ $this, 'newInstance' ]; 54 | 55 | return array_map( 56 | function ( array $args ) use ( $phpFails ) { 57 | return [ call_user_func_array( $phpFails, $args ), $args ]; 58 | }, 59 | $this->validConstructorProvider() 60 | ); 61 | } 62 | 63 | abstract public function validConstructorProvider(); 64 | 65 | /** 66 | * @dataProvider validConstructorProvider 67 | * 68 | * @since 3.0 69 | */ 70 | public function testGivenValidArguments_constructorDoesNotThrowException() { 71 | $instance = call_user_func_array( [ $this, 'newInstance' ], func_get_args() ); 72 | $this->assertInstanceOf( $this->getClass(), $instance ); 73 | } 74 | 75 | /** 76 | * @dataProvider invalidConstructorProvider 77 | * 78 | * @since 3.0 79 | */ 80 | public function testGivenInvalidArguments_constructorThrowsException() { 81 | $this->expectException( InvalidArgumentException::class ); 82 | call_user_func_array( [ $this, 'newInstance' ], func_get_args() ); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /tests/Unit/LegacyModel/CircleTest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class CircleTest extends BaseElementTest { 17 | 18 | /** 19 | * @see BaseElementTest::getClass 20 | * 21 | * @since 3.0 22 | * 23 | * @return string 24 | */ 25 | public function getClass() { 26 | return Circle::class; 27 | } 28 | 29 | public function validConstructorProvider() { 30 | $argLists = []; 31 | 32 | $argLists[] = [ new LatLongValue( 4, 2 ), 42 ]; 33 | $argLists[] = [ new LatLongValue( 42, 2.2 ), 9000.1 ]; 34 | $argLists[] = [ new LatLongValue( 4, 2 ), 1 ]; 35 | $argLists[] = [ new LatLongValue( 4, 2 ), 0.1 ]; 36 | 37 | return $argLists; 38 | } 39 | 40 | public function invalidConstructorProvider() { 41 | $argLists = []; 42 | 43 | $argLists[] = [ new LatLongValue( 4, 2 ), 0 ]; 44 | $argLists[] = [ new LatLongValue( 4, 2 ), -42 ]; 45 | 46 | return $argLists; 47 | } 48 | 49 | /** 50 | * @dataProvider instanceProvider 51 | * 52 | * @param Circle $circle 53 | * @param array $arguments 54 | */ 55 | public function testGetCircleCentre( Circle $circle, array $arguments ) { 56 | $this->assertTrue( $circle->getCircleCentre()->equals( $arguments[0] ) ); 57 | } 58 | 59 | /** 60 | * @dataProvider instanceProvider 61 | * 62 | * @param Circle $circle 63 | * @param array $arguments 64 | */ 65 | public function testGetCircleRadius( Circle $circle, array $arguments ) { 66 | $this->assertEquals( $arguments[1], $circle->getCircleRadius() ); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /tests/Unit/LegacyModel/ImageOverlayTest.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class ImageOverlayTest extends TestCase { 21 | 22 | public function testGetImage() { 23 | $imageOverlay = new ImageOverlay( 24 | new LatLongValue( 4, 2 ), 25 | new LatLongValue( -4, -2 ), 26 | 'Foo.png' 27 | ); 28 | 29 | $this->assertSame( 'Foo.png', $imageOverlay->getImage() ); 30 | } 31 | 32 | public function testGivenMetaData_overlayHasProvidedMetaData() { 33 | $parser = new ImageOverlayParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) ); 34 | 35 | $overlay = $parser->parse( "1,2:3,4:https://such.an/image.png~Semantic MediaWiki~World domination imminent!~https://such.link" ); 36 | 37 | $this->assertSame( 'https://such.an/image.png', $overlay->getImage() ); 38 | $this->assertSame( 'Semantic MediaWiki', $overlay->getTitle() ); 39 | $this->assertSame( 'World domination imminent!', $overlay->getText() ); 40 | $this->assertSame( 'https://such.link', $overlay->getLink() ); 41 | } 42 | 43 | public function testGivenLinkWithPrefix_linkIsParsedAndPrefixIsRemoved() { 44 | $parser = new ImageOverlayParser( new CoordinateFriendlyGeocoder( new NullGeocoder() ) ); 45 | 46 | $overlay = $parser->parse( "1,2:3,4:https://such.an/image.png~Semantic MediaWiki~World domination imminent!~link:https://such.link" ); 47 | 48 | $this->assertSame( 'https://such.link', $overlay->getLink() ); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /tests/Unit/LegacyModel/LineTest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class LineTest extends BaseElementTest { 17 | 18 | /** 19 | * @see BaseElementTest::getClass 20 | * 21 | * @since 3.0 22 | * 23 | * @return string 24 | */ 25 | public function getClass() { 26 | return Line::class; 27 | } 28 | 29 | public function validConstructorProvider() { 30 | $argLists = []; 31 | 32 | $argLists[] = [ [] ]; 33 | $argLists[] = [ [ new LatLongValue( 4, 2 ) ] ]; 34 | 35 | $argLists[] = [ 36 | [ 37 | new LatLongValue( 4, 2 ), 38 | new LatLongValue( 2, 4 ), 39 | new LatLongValue( 42, 42 ), 40 | ] 41 | ]; 42 | 43 | return $argLists; 44 | } 45 | 46 | public function invalidConstructorProvider() { 47 | $argLists = []; 48 | 49 | $argLists[] = [ [ '~=[,,_,,]:3' ] ]; 50 | $argLists[] = [ [ new LatLongValue( 4, 2 ), '~=[,,_,,]:3' ] ]; 51 | $argLists[] = [ [ '~=[,,_,,]:3', new LatLongValue( 4, 2 ) ] ]; 52 | 53 | return $argLists; 54 | } 55 | 56 | /** 57 | * @dataProvider instanceProvider 58 | * 59 | * @param Line $line 60 | * @param array $arguments 61 | */ 62 | public function testGetLineCoordinates( Line $line, array $arguments ) { 63 | $coordinates = $line->getLineCoordinates(); 64 | 65 | $this->assertIsArray( $coordinates ); 66 | $this->assertEquals( count( $arguments[0] ), count( $coordinates ) ); 67 | 68 | foreach ( $coordinates as $geoCoordinate ) { 69 | $this->assertInstanceOf( LatLongValue::class, $geoCoordinate ); 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /tests/Unit/LegacyModel/LocationTest.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class LocationTest extends TestCase { 18 | 19 | public function latLongValueProvider() { 20 | $argLists = []; 21 | 22 | $argLists[] = [ new LatLongValue( 0, 0 ) ]; 23 | $argLists[] = [ new LatLongValue( 4, 2 ) ]; 24 | $argLists[] = [ new LatLongValue( 42, 42 ) ]; 25 | $argLists[] = [ new LatLongValue( -4.2, -42 ) ]; 26 | 27 | return $argLists; 28 | } 29 | 30 | /** 31 | * @dataProvider latLongValueProvider 32 | */ 33 | public function testGivenLatLongInConstructor_getCoordinatesReturnsIt( LatLongValue $latLong ) { 34 | $location = new Location( $latLong ); 35 | $this->assertTrue( $latLong->equals( $location->getCoordinates() ) ); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tests/Unit/LegacyModel/PolygonTest.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class PolygonTest extends LineTest { 16 | 17 | /** 18 | * @see BaseElementTest::getClass 19 | * 20 | * @since 3.0 21 | * 22 | * @return string 23 | */ 24 | public function getClass() { 25 | return Polygon::class; 26 | } 27 | 28 | /** 29 | * @dataProvider instanceProvider 30 | */ 31 | public function testSetOnlyVisibleOnHover( Polygon $polygon ) { 32 | $this->assertFalse( $polygon->isOnlyVisibleOnHover() ); 33 | 34 | $polygon->setOnlyVisibleOnHover( true ); 35 | $this->assertTrue( $polygon->isOnlyVisibleOnHover() ); 36 | 37 | $polygon->setOnlyVisibleOnHover( false ); 38 | $this->assertFalse( $polygon->isOnlyVisibleOnHover() ); 39 | } 40 | 41 | /** 42 | * @dataProvider instanceProvider 43 | */ 44 | public function testSetFillOpacity( Polygon $polygon ) { 45 | $polygon->setFillOpacity( '0.42' ); 46 | $this->assertHasJsonKeyWithValue( $polygon, 'fillOpacity', '0.42' ); 47 | } 48 | 49 | protected function assertHasJsonKeyWithValue( Polygon $polygon, $key, $value ) { 50 | $json = $polygon->getJSONObject(); 51 | 52 | $this->assertArrayHasKey( $key, $json ); 53 | $this->assertEquals( 54 | $value, 55 | $json[$key] 56 | ); 57 | } 58 | 59 | /** 60 | * @dataProvider instanceProvider 61 | */ 62 | public function testSetFillColor( Polygon $polygon ) { 63 | $polygon->setFillColor( '#FFCCCC' ); 64 | $this->assertHasJsonKeyWithValue( $polygon, 'fillColor', '#FFCCCC' ); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /tests/Unit/LegacyModel/RectangleTest.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class RectangleTest extends BaseElementTest { 17 | 18 | /** 19 | * @see BaseElementTest::getClass 20 | * 21 | * @since 3.0 22 | * 23 | * @return string 24 | */ 25 | public function getClass() { 26 | return Rectangle::class; 27 | } 28 | 29 | public function validConstructorProvider() { 30 | $argLists = []; 31 | 32 | $argLists[] = [ new LatLongValue( 4, 2 ), new LatLongValue( -4, -2 ) ]; 33 | $argLists[] = [ new LatLongValue( -42, -42 ), new LatLongValue( -4, -2 ) ]; 34 | 35 | return $argLists; 36 | } 37 | 38 | public function invalidConstructorProvider() { 39 | $argLists = []; 40 | 41 | $argLists[] = [ new LatLongValue( 4, 2 ), new LatLongValue( 4, 2 ) ]; 42 | 43 | return $argLists; 44 | } 45 | 46 | /** 47 | * @dataProvider instanceProvider 48 | */ 49 | public function testGetCorners( Rectangle $rectangle, array $arguments ) { 50 | $this->assertTrue( $rectangle->getRectangleNorthEast()->equals( $arguments[0] ) ); 51 | $this->assertTrue( $rectangle->getRectangleSouthWest()->equals( $arguments[1] ) ); 52 | } 53 | 54 | /** 55 | * @dataProvider instanceProvider 56 | */ 57 | public function testSetCorners( Rectangle $rectangle ) { 58 | $coordinates = [ 59 | new LatLongValue( 42, 42 ), 60 | new LatLongValue( 0, 0 ) 61 | ]; 62 | 63 | foreach ( $coordinates as $coordinate ) { 64 | $rectangle->setRectangleNorthEast( $coordinate ); 65 | $this->assertTrue( $rectangle->getRectangleNorthEast()->equals( $coordinate ) ); 66 | 67 | $rectangle->setRectangleSouthWest( $coordinate ); 68 | $this->assertTrue( $rectangle->getRectangleSouthWest()->equals( $coordinate ) ); 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /tests/Unit/Map/StructuredPopupTest.php: -------------------------------------------------------------------------------- 1 | assertSame( 17 | '

MyTitle

', 18 | $this->getHtml( 'MyTitle', [] ) 19 | ); 20 | } 21 | 22 | private function getHtml( string $titleHtml, array $propertyValues ) { 23 | return ( new StructuredPopup( $titleHtml, $propertyValues ) )->getHtml(); 24 | } 25 | 26 | public function testTitleAndListAreSeparated() { 27 | $this->assertStringContainsString( 28 | '
', 29 | $this->getHtml( 'MyTitle', [ 'P1' => 'V1' ] ) 30 | ); 31 | } 32 | 33 | public function testListItems() { 34 | $this->assertStringContainsString( 35 | 'P1: V1
P2: V2', 36 | $this->getHtml( 'MyTitle', [ 'P1' => 'V1', 'P2' => 'V2' ] ) 37 | ); 38 | } 39 | 40 | public function testLinksAreAllowed() { 41 | $this->assertStringContainsString( 42 | '
P1: P1', 43 | $this->getHtml( 'MyTitle', [ 'P1' => 'P1' ] ) 44 | ); 45 | } 46 | 47 | public function testImagesAreAllowed() { 48 | $this->assertStringContainsString( 49 | '', 50 | $this->getHtml( 'MyTitle', [ 'P1' => '' ] ) 51 | ); 52 | } 53 | 54 | public function testRandomTagsAreFiltered() { 55 | $this->assertStringContainsString( 56 | 'P1: abcde', 57 | $this->getHtml( 'MyTitle', [ 'P1