Modul:GeoData
![]() | This module is rated as ready for general use. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by sandbox testing rather than repeated trial-and-error editing. |
![]() | Dieses Modul benutzt die Wikidata-Eigenschaften:
|
Dieses Modul enthält Funktionen zur Anzeige von geografischen Koordinaten für die Vorlagen {{Coord}} und {{GeoData}}.
Die Modulbeschreibung befindet sich in....
Lua-Fehler in Modul:Failsafe, Zeile 62: attempt to index field 'wikibase' (a nil value)
Benötigte weitere Module
Dieses Modul benötigt folgende weitere Module: Coordinates • CountryData • GeoData/i18n • GeoData/Params • Great circle distance • Wikidata utilities
-- Functions for the presentation of locations coordinate pairs -- module variable and administration local gd = { moduleInterface = { suite = 'GeoData', serial = '2022-10-22', item = 94472936 } } -- module import -- require( 'strict' ) local cd = require( 'Module:Coordinates' ) local cm = require( 'Module:CountryData' ) local gc = require( 'Module:Great circle distance' ) local gi = require( 'Module:GeoData/i18n' ) local gp = require( 'Module:GeoData/Params' ) local lp = require( 'Module:LinkPhone' ) local wu = require( 'Module:Wikidata utilities' ) local possibleFormats = { 'f1', 'f2', 'f3', 'f4', 'dec' } local lengthUnits = { ["1"] = { 'm', 1 }, Q11573 = { 'm', 1 }, Q828224 = { 'km', 1000 }, Q174789 = { 'mm', 0.001 }, Q218593 = { 'in', 0.0254 }, Q253276 = { 'mi', 1610 }, Q3710 = { 'ft', 0.3048 }, Q174728 = { 'cm', 0.01 }, Q848856 = { 'dam', 10 }, Q200323 = { 'dm', 0.1 } } local scalesByType = { adm1st = 1000000, adm2nd = 300000, adm3rd = 100000, airport = 30000, city = 100000, country = 10000000, edu = 10000, event = 50000, forest = 50000, glacier = 50000, isle = 100000, landmark = 10000, mountain = 100000, pass = 10000, railwaystation = 10000, river = 100000, satellite = 10000000, waterbody = 100000, camera = 10000, default = 300000 } -- zoom level 19 -> 1:1000, 0 -> 500000000 local maxZoomLevel = 19 local scales = { 1000, 2000, 4000, 8000, 15000, 35000, 70000, 150000, 250000, 500000, 1000000, 2000000, 4000000, 10000000, 15000000, 35000000, 70000000, 150000000, 250000000, 500000000 } -- Local helper functions -- Helper function isSet local function isSet( param ) return param and param ~= ''; end local function setValue( value, default ) return isSet( value ) and value or default end -- Helper function roundScale local function roundScale( scale ) if scale <= scales[ 1 ] then return scales[ 1 ] end for i = 2, #scales, 1 do if scale > scales[ i - 1 ] and scale <= scales[ i ] then return scales[ i ] end end return scales[ #scales ] end -- Helper function getZoomFromScale local function getZoomFromScale( scale ) if scale <= scales[ 1 ] then return maxZoomLevel end for i = 2, #scales, 1 do if scale > scales[ i - 1 ] and scale <= scales[ i ] then return maxZoomLevel + 2 - i -- because i starts from 2 end end return 0 end -- helper function round -- n: value to round -- idp: number of digits after the decimal point local function round( n, idp ) local m = 10^( idp or 0 ) if n >= 0 then return math.floor( n * m + 0.5 ) / m else return math.ceil( n * m - 0.5 ) / m end end local function checkFormat( f ) if type( f ) == 'table' then return f end for i, fmt in ipairs( possibleFormats ) do if fmt == f then return f end end return 'f1' end local function checkNumber( s ) return tonumber( s ) or '' end -- helper function getPrecision -- returns integer precision number -- possible values: numbers, D, DM, DMS local function getPrecision( prec ) local p = tonumber( prec ) if p then p = round( p, 0 ) if p < -1 then p = -1 elseif p == 1 then p = 2 elseif p == 3 then p = 4 elseif p > 6 then p = 6 -- maximum 6 decimals end return p else p = prec and prec:upper() or 'DMS' if p == 'D' then return 0 elseif p == 'DM' then return 2 elseif p == 'DMS' then return 4 else return '' end end end -- getPrecisionFromSize gives precision number for toDMS function calculated -- from the maximum of the dimension of a geographic object or the measurement -- error of the coordinate (as a radius) -- 1° equals about 1852 meters -- 1852 meters give precision = 2 (1') local function getPrecisionFromSize( dim, err ) local d = tonumber( dim ) or 4000 if err ~= '' then local m = tonumber( err ) or 0 if m > d / 2 then d = 2 * m end end if d < 1 then d = 1 end -- 2 * 60 * 1852 = 222240 d = math.ceil ( math.log10 ( 222240 / d ) ) if d < 0 then d = 0 elseif d > 5 then d = 5 end if d == 1 then d = 2 elseif d == 3 then d = 4 end return d end -- getExtraParameters returns a string with extra Geohack parameters as it is -- used by Special:Mapsources and {{#coordinates}} local function getExtraParameters( args ) local s = 'scale:' .. args.scale if args.type ~= '' then s = s .. '_type:' .. args.type end if args.dim ~= '' then s = s .. '_dim:' .. args.dim end s = s .. '_globe:' .. args.globe if args.region ~= '' then s = s .. '_region:' .. args.region end return s end -- Helper function for microformat creation local function getMicroformat( aName, addClasses ) return '<span class="h-card' .. ( isSet( addClasses ) and ( ' ' .. addClasses ) or '' ) .. '" style="display: none;">' .. '<span class="p-geo geo">' .. '<span class="p-latitude latitude">$5</span>, ' .. '<span class="p-longitude longitude">$6</span>' .. '</span>' .. '<span class="p-name">' ..aName .. '</span>' .. '</span>' end -- Coordinates shown as article indicator -- Helper function indicatorTable returns a table containing coordinate text inset local function indicatorTable( inset, aName, zoom, lat, long, country ) local function data( s ) return isSet( s ) and s or nil end local c = mw.ustring.gsub( gi.titles.coordinates, '($1)', aName) local m = mw.ustring.gsub( gi.titles.mapsources, '($1)', aName) local adm1st if country.id ~= '' then adm1st = country.adm1st or cm.getAdm1st( country.id ) inset = inset:gsub( '_region%%3A%a+&', '_region%%3A' .. ( adm1st or country.iso_3166 ) .. '&' ) end local html = mw.html.create( 'div' ) :addClass( 'voy-coord-indicator' ) :attr( 'data-zoom', zoom ) :attr( 'data-lat', lat ) :attr( 'data-lon', long ) :node( mw.html.create( 'div' ) :addClass( 'voy-icon' ) :attr( 'title', c ) :node( mw.html.create( 'span' ) :addClass( 'voy-map-globe-default' ) :wikitext( gi.titles.globeDefault ) ) :node( mw.html.create( 'span' ) :addClass( 'voy-map-globe-js' ) :css( 'display', 'none' ) :wikitext( gi.titles.globeJS ) ) ) :node( mw.html.create( 'div' ) :attr( 'class', 'voy-coords printNoLink plainlinks' ) :attr( 'title', m ) :wikitext( inset ) ) -- adding country- and region-related data if country.id ~= '' then if country.cc ~= '' then country.trunkPrefix = lp.getTrunkPrefix( country.cc ) end html:attr( 'data-country', data( country.iso_3166 ) ) :attr( 'data-country-name', data( country.country ) ) :attr( 'data-adm1st', adm1st ) :attr( 'data-country-calling-code', data( country.cc ) ) :attr( 'data-trunk-prefix', data( country.trunkPrefix ) ) :attr( 'data-lang', data( country.lang ) ) :attr( 'data-lang-name', data( country.langName ) ) :attr( 'data-currency', data( country.addCurrency ) ) :attr( 'data-dir', data( country.isRTL and 'rtl' or 'ltr' ) ) end return tostring( html ) end -- getLengthFromWD returns a length by property from WD local function getLengthFromWD( id, p ) local w = wu.getValue( id, p ) if w == '' then return 0 end local a = tonumber( w.amount ) or 0 local u = lengthUnits[ w.unit ] if u then u = u[ 2 ] else u = wu.getValue( w.unit, gi.properties.conversionToSI ) u = u == '' and 0 or tonumber( u.amount ) or 0 end return a * u end -- Helper function to get data from Wikidata by id -- gets name, dimension, latitude, and longitude -- returns an error if no coordinates are available local function getParamsFromWD( args ) local coordState = { err = not isSet( args.lat ) or not isSet( args.long ), faultyCoordinate = false, distanceErr = 0, fromWD = false } if not coordState.err then local objLat = cd.toDec( args.lat, 'lat', 8 ) args.lat = objLat.dec local objLong = cd.toDec( args.long, 'long', 8 ) args.long = objLong.dec coordState.err = ( objLat.error + objLong.error ) > 0 coordState.faultyCoordinate = coordState.err end if not args.wikidata then return args, coordState end if not isSet( args.name ) then args.name = mw.wikibase.label( args.wikidata ) or '' end local length = math.abs( tonumber( args.dim ) or 0 ) if length < 1 then length = getLengthFromWD( args.wikidata, gi.properties.length ) local width = getLengthFromWD( args.wikidata, gi.properties.width ) if width > length then length = width end end args.dim = length < 1 and '' or round( length ) -- getting coordinates in any case local c = wu.getValue( args.wikidata, gi.properties.centerCoordinates ) if c == '' then c = wu.getValue( args.wikidata, gi.properties.coordinates ) end if c ~= '' then c.latitude = tonumber( c.latitude ) c.longitude = tonumber( c.longitude ) -- 1° ~ 1842 m c.precision = ( tonumber( c.precision ) or 0 ) * 1842 if not isSet( args.prec ) and c.precision >= 1 then args.prec = round( c.precision ) end if coordState.err then args.lat = c.latitude args.long = c.longitude coordState.err = false coordState.faultyCoordinate = false coordState.fromWD = true else -- GeoData and Wikidata positions may differ coordState.distanceErr = gc.getGcd( args.lat, args.long, c.latitude, c.longitude ) end end return args, coordState end local function makeCoordinates( args, pattern ) local s, lat, long if args.format == 'dec' then s, lat, long = cd.getDecGeoLink( pattern, args.lat, args.long, args.precision ) else s, lat, long = cd.getGeoLink( pattern, args.lat, args.long, '', '', '', '', args.precision, args.format ) end return s end -- Display the coordinates local function displayCoords( args, frame, isNs0 ) args.lat = args[ 1 ] or args.lat or args.NS or '' -- NS/EW: fallback args.long = args[ 2 ] or args.long or args.EW or '' args.name = args.name or '' args.display = args.display and args.display:lower() or 'inline' args.format = checkFormat( args.format and args.format:lower() or 'f1' ) args.addMf = args.addMf and args.addMf:lower() or '' -- if 'true' then add microformat and {{#coordinates}} args.region = args.region and args.region:upper() or '' args.dim = checkNumber( args.dim or '' ) args.globe = args.globe and args.globe:lower() or 'earth' args.precision = getPrecision( args.precision or '' ) -- display precission args.prec = checkNumber( args.prec or '' ) -- measurement error args.radius = checkNumber( args.radius or '' ) args.scale = checkNumber( args.scale or '' ) args.zoom = checkNumber( args.zoom or '' ) args.type = args.type and args.type:lower() or '' if not isSet( args.wikidata ) or not mw.wikibase.isValidEntityId( args.wikidata ) then args.wikidata = nil end if not scalesByType[ args.type ] then args.type = '' end local cat = '' -- tracking categories -- Parameter check local function fIsInline( s ) -- inline in any case return s:find( 'inline' ) or s == 'i' or s == 'it' or s == 'ti' end local isInline = fIsInline( args.display ) local function fIsInTitle( s ) -- intitle in any case return s:find( 'title' ) or s == 't' or s == 'it' or s == 'ti' end local isInTitle = fIsInTitle( args.display ) if not isInline and not isInTitle then isInline = true end if isInTitle then if not args.wikidata then args.wikidata = mw.wikibase.getEntityIdForCurrentPage() end if not isSet( args.name ) then args.name = mw.title.getCurrentTitle().subpageText end end local coordState args, coordState = getParamsFromWD( args ) if coordState.err then local m = coordState.faultyCoordinate and gi.errorMsg.faultyCoordinate or '' if isInTitle then return m .. gi.categories.geoWithoutCoords else return m .. gi.categories.coordWithoutCoords end end if coordState.distanceErr > 50 then cat = cat .. gi.categories.differentPositions50 elseif coordState.distanceErr > 25 then cat = cat .. gi.categories.differentPositions25 elseif coordState.distanceErr > 10 then cat = cat .. gi.categories.differentPositions end if coordState.fromWD then cat = cat .. gi.categories.fromWikidata end local country = cm.getCountryData( args.wikidata, nil ) if country.fromWD then cat = cat .. gi.categories.countryFromWD end if not isSet( args.region ) then args.region = country.iso_3166 end if args.name == '' then args.name = gi.errorMsg.missingName cat = cat .. gi.categories.coordWithoutName end if args.scale == '' and args.type ~= '' then args.scale = scalesByType[ args.type ] end if args.zoom ~= '' then args.zoom = round( args.zoom ) if args.zoom < 0 and args.zoom > maxZoomLevel then args.zoom = '' end end if args.dim == '' and args.radius == '' and args.zoom == '' and args.scale == '' then args.dim = 4000 cat = cat .. gi.categories.withoutScale end if args.dim == '' and args.radius ~= '' then args.dim = round( 2 * args.radius ) end if args.scale == '' and args.zoom ~= '' then args.scale = scales[ maxZoomLevel + 1 - args.zoom ] end if args.dim == '' and args.scale ~= '' then args.dim = args.scale / 5 end if args.scale == '' then args.scale = 5 * args.dim end args.scale = roundScale( args.scale ) if args.precision == '' then -- mainly for geo/geoData args.precision = getPrecisionFromSize( args.dim, args.prec ) end local extra = getExtraParameters( args ) local mf = '' if args.addMf == 'true' then -- adding microformat mf = getMicroformat( args.name, 'listing-coordinates' ) end -- inline coordinates local v = '' if isInline then v = '<span class="printNoLink plainlinks' .. '">' .. mf .. '[' .. gi.coordURL .. '$1_$2_' .. mw.uri.encode( extra, 'QUERY' ) .. '&locname=' .. mw.uri.encode( args.name, 'QUERY' ) .. ' <span class="voy-coord-style" title="' .. gi.titles.latitude .. '">$3</span>' .. ' <span class="voy-coord-style" title="' .. gi.titles.longitude .. '">$4</span>]' .. '</span>' v = makeCoordinates( args, v ) end -- indicator/in title coordinates local w = '' if isInTitle then w = mf .. '[' .. gi.coordURL .. '$1_$2_' .. mw.uri.encode( extra, 'QUERY' ) .. '&locname=' .. mw.uri.encode( args.name, 'QUERY' ) .. ' $3<br />$4]' w = makeCoordinates( args, w ) w = indicatorTable( w, args.name, getZoomFromScale( args.scale ), args.lat, args.long, country ) end local parserFc = '' local parserFcArgs = { args.lat, args.long, extra, name = args.name } if isInTitle then table.insert( parserFcArgs, 1, 'primary' ) end if frame and args.addMf == 'true' then -- adding {{#coordinates}} parserFc = frame:callParserFunction{ name = '#coordinates', args = parserFcArgs } end v = v .. w .. parserFc .. cat .. ( isNs0 and wu.getCategories( gi.categories.properties ) or '' ) return v end -- [[template:Coord]] template -- format: f1 ... f4, dec function gd.coord( frame ) local args = frame:getParent().args local ns = mw.title.getCurrentTitle().namespace args.display = 'inline' args.precision = setValue( args.precision, '4' ) args.addMf = 'true' -- with {{#coordinates}} and microformat return displayCoords( args, frame, ns == 0 ) .. gp.checkParams( args, gp.coord ) end -- [[template:Geo]] template -- if id is set then data are alternatively used from Wikidata -- includes primary {{#coordinate}} -- calculates precission from the size of the geographical object function gd.geo( frame ) local args = frame:getParent().args local title = mw.title.getCurrentTitle() local ns = title.namespace local categs = '' args.display = 'title' args.format = 'f2' args.name = setValue( args.name, title.subpageText ) args.addMf = frame.args.minerva and 'false' or 'true' -- prevent 2nd #coordinates call with Minerva skin if ns == 0 then categs = categs .. gi.categories.hasGeo end return displayCoords( args, frame, ns == 0 ) .. gp.checkParams( args, gp.geo ) .. categs end return gd
Kategorien:
- Seiten mit Skriptfehlern
- Modules for general use
- Module:Module, die Wikidata benutzen
- Modul benötigt das Modul Coordinates
- Modul benötigt das Modul CountryData
- Modul benötigt das Modul GeoData/i18n
- Modul benötigt das Modul GeoData/Params
- Modul benötigt das Modul Great circle distance
- Modul benötigt das Modul Wikidata utilities
- Vorlagen:Koordinaten