Dokumentation für das Modul Mapframe[Ansicht] [Bearbeiten] [Versionsgeschichte] [Aktualisieren]

Anwendung

Der Modul wird direkt von der Vorlage {{Mapframe}} aufgerufen. Parameterbeschreibung siehe dort.

Versionsbezeichnung auf Wikidata: 2024-01-26 Ok!

Benötigte weitere Module

Dieses Modul benötigt folgende weitere Module: Coordinates • Mapframe/Params • Mapshape utilities • Mapshape utilities/i18n • Marker utilities/Groups • Yesno • Wikidata utilities
Hinweise
-- inserting a mapframe map -- This edition was made for the special needs of Wikivoyage. For a use at a -- Wikipedia please use https://en.wikipedia.org/wiki/Module:Mapframe  -- documentation local Mapframe = { 	suite  = 'Mapframe', 	serial = '2024-01-26', 	item   = 52554979 }  -- module import -- require( 'strict' ) local cd = require( 'Module:Coordinates' ) local mg = mw.loadData( 'Module:Marker utilities/Groups' ) local mi = require( 'Module:Mapshape utilities/i18n' ) local mp = require( 'Module:Mapframe/Params' ) local mu = require( 'Module:Mapshape utilities' ) local wu = require( 'Module:Wikidata utilities' ) local yn = require( 'Module:Yesno' )  -- module variable and administration local mf = { 	content  = {}, 	entityId = nil, 	wikilang = nil, 	moduleInterface = Mapframe }  -- return decimal coordinate if possible local function toDec( coord, dir ) 	if mu.isSet( coord ) then 		local t = cd.toDec( coord, dir, 6 ) 		if t.error == 0 then 			return t.dec 		end 	end 	return nil end  -- split coordinates to latitude and longitude local function _parseCoords( coords ) 	local lat = nil 	local long = nil 	if not mu.isSet( coords ) then 		return nil, nil 	end  	coords = coords:upper() 	local count 	if not coords:find( '[,]' ) and coords:find( '[NS]' ) then 		coords, count = coords:gsub( '([NS])', '%1,' ) -- adding separator 	end 	local parts = mw.text.split( coords, ',', true ) 	if #parts == 2 or #parts == 3 then -- 3: including elevation 		lat = mw.text.trim( parts[ 1 ] ) 		long = mw.text.trim( parts[ 2 ] ) 		-- check for mask borders 		if lat:find( '36000', 1, true ) and long:find( '180', 1, true ) then 			lat = tonumber( lat ) 			long = tonumber( long ) 		else 			lat = toDec( lat, 'lat' ) 			long = toDec( long, 'long' ) 		end 		if not lat or not long then 			return nil, nil 		end 	end 	return lat, long end  -- parse set of coordinates -- programmer of function: user Yurik (Yuri Astrakhan), see Modul:Map local function parseCoords( geoType, coords ) 	local geoTypes = { 		Point           = { levels = 1, min = 1 }, 		MultiPoint      = { levels = 1, min = 2 }, 		LineString      = { levels = 1, min = 2 }, 		MultiLineString = { levels = 2, min = 2 }, 		Polygon         = { levels = 2, min = 4 }, 		MultiPolygon    = { levels = 3, min = 4 } 	} 	local levels = geoTypes[ geoType ].levels 	local min = geoTypes[ geoType ].min  	local results = {} 	for i = 1, levels, 1 do 		results[ i ] = {} 	end 	local gap = 0 	local errors = ''  	local closeArrays = function( gap ) 		if #results[ levels ] < min then 			errors = errors .. mw.ustring.format( mi.mfMinValues, min ) .. ' ' 		elseif min == 1 and #results[ levels ] ~= 1 then -- Point 			errors = errors .. mi.mfExactlyOne .. ' '         end         for i = levels, levels-gap+1, -1 do             table.insert( results[ i-1 ], results[ i ] )             results[ i ] = {}         end         return 0 -- reset gap     end  	local points = mw.text.split( coords, ';', true ) 	local lat, long, val 	for i = 1, #points, 1 do 		val = mw.text.trim( points[ i ] ) 		if val == '' then 			gap = gap + 1 			if gap >= levels then 				errors = errors .. mi.mfTooManyLevels .. ' ' 			end 		else 			lat, long = _parseCoords( val ) 			if lat and long then 				if gap > 0 then 					gap = closeArrays( gap ) 				end 				table.insert( results[ levels ], { long, lat } ) 			else 				errors = errors .. mi.mfBadData .. ' ' 			end 		end 	end 	closeArrays( levels - 1 ) 	if errors == '' then 		return geoType == 'Point' and results[ 1 ][ 1 ] or results[ 1 ], errors 	else 		return nil, errors 	end end  -- get individual coordinate from Wikidata local function getWdCoords( id ) 	if mu.isSet( id ) then 		local c = wu.getValue( id, 'P625' ) 		if c ~= '' then 			return c.latitude, c.longitude 		end 	end 	return nil, nil end  -- preparing title and description for geoJSON object local function getTitle( title, description, image, firstId, ids, service ) 	local function geomaskTitle() 		if service == 'geomask' then 			title = mw.ustring.format( mi.geomask, title ) 		end 	end 	-- getting title if only one id 	if title == '' then 		title = nil 	end 	if mu.isSet( firstId ) and firstId == ids then 		title = mu.addLink( title or mu.getTitle( firstId ), firstId, 			mf.entityId, mf.wikiLang ) 		geomaskTitle() 		if not mu.isSet( description ) and not mu.isSet( image ) then 			image = mu.getImage( firstId ) 		end 	else 		title = title or mw.title.getCurrentTitle().subpageText 		geomaskTitle() 	end 	if not mu.isSet( description ) and mu.isSet( image ) then 		description = '[[File:' .. image .. '|100x100px]]' 	end 	return title, description end  -- check for mapshapes Wikidata ids local function idMatch( only, exclude, id ) 	only = only or '' 	exclude = exclude or '' 	if only == '' and exclude == '' then 		return true  	end  	local function isIn( list ) 	 	local parts = mw.text.split( list, ',', true ) 		for i = 1, #parts, 1 do 			if mw.text.trim( id ) == mw.text.trim( parts[ i ] ) then 				return true 			end 		end 		return false 	end   	if only ~= '' then  		return isIn( only ) 	else 		return not isIn( exclude ) 	end end  -- make GeoJSON object for tag call local function makeGeoJSON( args, argIndex ) 	local service = '' 	for key, value in pairs( mp.services ) do 		for key2, value2 in ipairs( value ) do 			if args.service == value2 then 				service = key 				break 			end 		end 		if service ~= '' then 			break 		end 	end 	if service == '' then 		return mi.mfNoService 	end  	local world = '36000,-180;36000,180;-36000,180;-36000,-180;36000,-180;;' 	local coordinates, description, errors, firstId, geojson, geoType, i, id 	local ids, image, lat, link, long, properties, rgb, stroke, title, values  	-- default title and description 	if service ~= 'page' then 		title = args.title 		description = args.description 		image = args.image 	end  	-- processing coordinate data instead of Wikidata IDs 	args.coord = mw.ustring.gsub( args.coord or '', ';*$', '' ) 	if mu.isSet( args.coord ) and service ~= 'page' and service ~= 'shapes' then 		if mu.isSet( args.wikidata ) then 			return mi.mfTogether 		end 		if service == 'point' then 			args.coord = mw.ustring.gsub( args.coord, ';;*', ';' ) 			if mw.ustring.find( args.coord, ';', 1, true ) then 				geoType = 'MultiPoint' 			else 				geoType = 'Point' 			end 		elseif service == 'geoline' then 			args.coord = mw.ustring.gsub( args.coord, ';;;*', ';;' ) 			if mw.ustring.find( args.coord, ';;', 1, true ) then 				geoType = 'MultiLineString' 			else 				geoType = 'LineString' 			end 		elseif service == 'geoshape' or service == 'geomask' then 			if service == 'geomask' then 				args.coord = world .. args.coord 			end 			args.coord = mw.ustring.gsub( args.coord, ';;;;*', ';;;' ) 			if mw.ustring.find( args.coord, ';;', 1, true ) then 				geoType = 'MultiPolygon' 			else 				geoType = 'Polygon' 			end 		end 		coordinates, errors = parseCoords( geoType, args.coord ) 		if not coordinates then 			return errors 		end  		title, description = 			getTitle( title, description, image, nil, nil, service )  		if geoType == 'Point' or geoType == 'MultiPoint' then 			properties = { 				title = title, 				description = description, 				[ 'marker-symbol' ] = mu.getParameter( args.marker, nil ), 				[ 'marker-color' ] = mu.getParameter( args.markerColor, mi.defaultMarkerColor ) 			} 		else 			stroke = args.stroke or '' 			if stroke == '' then 				stroke = mi.defaultStroke 			end 			properties = { 				title = title, 				description = description, 				fill = mu.getParameter( args.fill, mi.defaultFill ), 				[ 'fill-opacity' ] = mu.getNumber( args.fillOpacity, mi.defaultFillOpacity ), 				stroke = stroke, 				[ 'stroke-width' ] = mu.getNumber( args.strokeWidth, mi.defaultStrokeWidth ), 				[ 'stroke-opacity' ] = mu.getNumber( args.strokeOpacity, mi.defaultStrokeOpacity ) 			} 		end 		geojson = { 			type = 'Feature', 			geometry = { 				type = geoType, 				coordinates = coordinates 			}, 			properties = properties 		}		 		table.insert( mf.content, mw.text.jsonEncode( geojson ) ) 		return '' -- no errors 	end 	 	-- processing Wikidata and Wikimedia Commons data 	if service == 'shapes' and not mi.excludeOSM then	 		-- in case of shapes multiple GeoJSON objects are returned 		if not mu.isSet( args.wikidata ) then 			return mi.mfNoWikidata 		end  		values = mu.getMapshapes( args.wikidata ) 		if #values == 0 then 			return mi.mfNoParts 		end  		args.defaultType = mu.getParameter( args.defaultType, 'geoline' ) 		stroke = args.stroke or '' 		if stroke == '' then 			stroke = args.defaultColor or '' 		end 		if stroke == '' then 			stroke = mi.defaultStroke 		end 		if not string.find( stroke, '#', 1, true ) then 			stroke = '#' .. stroke 		end 				 		for i = 1, #values, 1 do 			id = values[ i ].id 			if idMatch( args.only, args.exclude, id ) then 				title = mu.addLink( mw.wikibase.label( id ) or id, id, 					mf.entityId, mf.wikiLang ) 				description = mu.getImage( id ) 				if description == '' then 					description = nil --				else --					description = '[[File:' .. description .. '|141px]]' 				end  				rgb = mu.getColor( id ) 				if rgb == '' then 					rgb = stroke 				end  				geojson = { 					type = 'ExternalData', 					service = args.defaultType, 					ids = id, 					properties = { 						title = title, 						description = description, 						fill = mu.getParameter( args.fill, mi.defaultFill ), 						[ 'fill-opacity' ] = mu.getNumber( args.fillOpacity, mi.defaultFillOpacity ), 						stroke = rgb, 						[ 'stroke-width' ] = mu.getNumber( args.strokeWidth, mi.defaultShapesWidth ), 						[ 'stroke-opacity' ] = mu.getNumber( args.strokeOpacity, mi.defaultShapesOpacity ) 					} 				} 				-- collecting multiple geojson codes 				table.insert( mf.content, mw.text.jsonEncode( geojson ) ) 			end 		end 		return '' -- objects already inserted, no errors  	elseif service == 'page' then -- data from Wikimedia Commons 		if args.commons then 			geojson = { 				type = 'ExternalData', 				service = 'page', 				title = mw.ustring.gsub( args.commons, '[Dd]ata:', '' ) 			} 		else 			return mi.mfNoCommons 		end  	elseif service == 'point' then 		ids = mw.text.split( args.wikidata, ',', true ) 		coordinates = {} 		for i = 1, #ids, 1 do 			id = mw.text.trim( ids[ i ] ) 			if id ~= '' and mw.wikibase.isValidEntityId( id ) then 				lat, long = getWdCoords( id ) 				if lat and long then 					table.insert( coordinates, { long, lat } ) 					if #coordinates == 1 then 						title, description = 							getTitle( title, description, image, id, id, service ) 					end 				end 			end 		end 		if #coordinates == 0 then 			return mi.mfNoWdCoord 		end  		i = #coordinates == 1 		geojson = { 			type = 'Feature', 			geometry = { 				type = i and 'Point' or 'MultiPoint', 				coordinates = i and coordinates[ 1 ] or coordinates 			}, 			properties = { 				title = title, 				description = description, 				[ 'marker-symbol' ] = mu.getParameter( args.marker, nil ), 				[ 'marker-color' ] = mu.getParameter( args.markerColor, mi.defaultMarkerColor ) 			} 		}  	-- geoline or geoshape/geomask 	elseif not mi.excludeOSM then 		if mu.isSet( args.wikidata ) then 			ids = args.wikidata 		else 			ids = mf.entityId 		end 		if not mu.isSet( ids ) then 			return mi.mfNoWikidata 		end  		-- getting first id 		firstId = mu.getFirstId( ids )  		title, description = 			getTitle( title, description, image, firstId, ids, service ) 				 		-- getting color from first id 		stroke = args.stroke or '' 		if stroke == '' then 			stroke = mu.getColor( firstId ) 			if stroke == '' then 				stroke = mi.defaultStroke 			end 		end 		if service == 'geoshape' and argIndex > 0 then 			args.fill = mu.getParameter( args.fill, mi.defaultColors[ argIndex ] ) 		end  		geojson = { 			type = 'ExternalData', 			service = service, 			ids = ids, 			properties = { 				title = title, 				description = description, 				fill = mu.getParameter( args.fill, mi.defaultFill ), 				[ 'fill-opacity' ] = mu.getNumber( args.fillOpacity, mi.defaultFillOpacity ), 				stroke = stroke, 				[ 'stroke-width' ] = mu.getNumber( args.strokeWidth, mi.defaultStrokeWidth ), 				[ 'stroke-opacity' ] = mu.getNumber( args.strokeOpacity, mi.defaultStrokeOpacity ) 			} 		} 	end 	 	table.insert( mf.content, mw.text.jsonEncode( geojson ) ) 	return '' end  -- processing multiple shape definitions local function makeTagContent( args ) 	local errors = '' 	local r = mu.getParameter( args.raw, nil ) 	if r then 		return r, false, errors 	end  	local err = false 	local commons, service, single, wikidata  	local function mergeArgs( indx ) 		service = args[ 'type' .. indx ] or '' 		commons = args[ 'page' .. indx ] or '' 		if commons ~= '' then 			service = 'page' 		end 		wikidata = args[ 'wikidata' .. indx ] or '' 		if service == '' and wikidata ~= '' then 			service = 'geomask' 		end 	end  	-- mapgroup parameters 	if args.groupWikidata ~= '' then 		single = { 			service = 'geomask', 			wikidata = args.groupWikidata, 			fill = args.fillMask 		} 		errors = errors .. makeGeoJSON( single, 0 ) 	end 	if args.highlightWikidata ~= '' then 		single = { 			service = 'geoshape', 			wikidata = args.highlightWikidata, 			fill = mu.getParameter( args.fill, mi.defaultHighlight ) 		} 		errors = errors .. makeGeoJSON( single, 0 ) 	end 	 	local argsIndex = '' 	mergeArgs( argsIndex ) 	while service ~= '' do 		-- remove index from args parameters and copy them to single 		single = { 			commons = commons, 			wikidata = wikidata, 			service = service 		} 		if commons ~= '' and wikidata ~= '' then 			err = true 		end 		for k, v in pairs( args ) do 			if v == '' then 				v = nil 			end 			if string.match( k, '^[%a%-]+' .. argsIndex .. '$' ) then 				single[ string.gsub( k, argsIndex, '' ) ] = v 			end 		end  		if argsIndex == '' then 			argsIndex = 1 		end 		errors = errors .. makeGeoJSON( single, argsIndex )  		argsIndex = argsIndex + 1 		mergeArgs( argsIndex ) 		-- stop if there is no service anymore 	end 	 	if #mf.content == 0 then 		return nil, err, errors 	elseif #mf.content == 1 then 		return mf.content[ 1 ], err, errors 	else 		return '[' .. table.concat( mf.content, ',') .. ']', err, errors 	end end  -- calling mapframe/maplink tag; addings shapes local function _mapframe( args, frame ) 	local tagArgs = {} 	mf.entityId = mw.wikibase.getEntityIdForCurrentPage() 	mf.wikiLang = mw.getContentLanguage():getCode()  	-- auto-center if tagArgs.latitude = nil or tagArgs.longitude = nil 	tagArgs.latitude = toDec( args.lat, 'lat' ) 	tagArgs.longitude = toDec( args.long, 'long' ) 	if not tagArgs.latitude or not tagArgs.longitude then 		if args.coords ~= '' then 			tagArgs.latitude, tagArgs.longitude = _parseCoords( args.coords ) 		else 			tagArgs.latitude = nil 			tagArgs.longitude = nil 		end 	end 	tagArgs.zoom = tonumber( args.zoom ) 	-- auto-zoom if tagArgs.zoom = nil 	if tagArgs.zoom then 		tagArgs.zoom = math.floor( tagArgs.zoom ) 		if tagArgs.zoom < 1 or tagArgs.zoom > mi.maxZoomLevel then 			tagArgs.zoom = nil 		end 	end 	if tagArgs.latitude and tagArgs.longitude and not tagArgs.zoom then 		tagArgs.zoom = mi.defaultZoom 	end 	if args.tagName == 'mapframe' then 		tagArgs.align = mu.getParameter( args.align, 'right' ) 		if args.width == 'full' then 			tagArgs.width = 'full' 			tagArgs.align = 'center' 		else 			tagArgs.width = mu.getSize( args.width, mi.defaultWidth ) 				+ mi.borderAdjustment -- 2px: inside borders 		end 		tagArgs.height = mu.getSize( args.height, mi.defaultHeight ) 	end 	tagArgs.show = args.show 	if tagArgs.show ~= '' then 		if args.group ~= '' then 			tagArgs.group = args.group 		else 			if not tagArgs.show:find( ',' ) then 				tagArgs.group = tagArgs.show 			else 				tagArgs.group = mi.defaultGroup 			end 		end 	else 		tagArgs.show = mg.showAll 		tagArgs.group = mu.checkGroup( args.group ) 	end 	tagArgs.group = mu.translateGroup( tagArgs.group ) 	if not mw.ustring.find( tagArgs.show, tagArgs.group ) then 		tagArgs.show = tagArgs.show .. ',' .. tagArgs.group 	end 	if yn( args.plain, false ) then 		tagArgs.frameless = '1' 	else 		tagArgs.text = args.name 		if tagArgs.text == '' and args.tagName == 'mapframe' then 			tagArgs.text = string.format( mi.mapOf, mw.title.getCurrentTitle().subpageText ) 		end 	end 	tagArgs.class = args.class 	if args.tagName == 'maplink' then 		if tagArgs.class == '' and ( tagArgs.text == '' or tagArgs.text == '""' ) then 			-- Hide pushpin icon in front of an empty text link 			tagArgs.class = 'no-icon'     	end 	end  	local tagContent, err, errors = makeTagContent( args )  	local result = frame:extensionTag( args.tagName, tagContent, tagArgs ) 	if err then 		result = result .. mi.mfTogether2 	end 	if mu.isSet( errors ) then 		result = result .. '<span class="error">' .. errors .. '</span>' 			.. mi.mfErrorCateg 	end 	-- adding maintenance categories 	if mw.title.getCurrentTitle().namespace == 0 then 		if mi.usePropertyCategs then 			result = result .. wu.getCategories( mi.properties ) 				.. mu.getCategories( mi.properties ) 		end 		if tagContent then 			result = result .. mi.mfWithShapes 		end 	end 	return result end  -- for Mapframe template function mf.mapframe( frame ) 	local args, errors = 		mu.checkParams( frame:getParent().args, mp.params, 'Mapframe', mi.mfUnknown ) 	args.tagName = 'mapframe' 	return _mapframe( args, frame ) .. errors end  -- for Maplink template function mf.maplink( frame ) 	local args, errors = 		mu.checkParams( frame:getParent().args, mp.params, 'Mapframe', mi.mfUnknown ) 	local isMapframe = yn( args.frame, false ) -- wp compatibility 	if isMapframe then 		args.tagName = 'mapframe' 	else 		args.tagName = 'maplink' 	end 	return _mapframe( args, frame ) .. errors end  return mf