Jump to content

Module:Mapdraw

From Wikivoyage

-- keeping original shape for a circle -- Matroc
-- other shapes etc. moved to a Sandbox

local p = {}

local function newlat(a)
-- newlat = math.log(math.tan((90 + a) * math.pi / 360)) / (math.pi / 180) -- worked this code elsewhere in function can remove
   newlatitude = 180/math.pi * (2 * math.atan(math.exp( a * math.pi/180)) - math.pi/2 )
  if newlatitude > 89.5 then point = 89.5 end -- END if       -- straight line at top of map
  if newlatitude < -89.5 then point = -89.5 end -- END if     -- straight line at bottom of map
  return newlatitude
end

local function checkhex(fill,stroke)
         if string.len(fill) ~= 7 then error("Incorrect length for argument fill!") end
         if string.gsub(fill,"#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]","") ~= "" then
            error("Incorrect hexidecimal format for argument fill!") end
         if string.len(stroke) ~= 7 then error("Incorrect length for argument stroke!") end
         if string.gsub(stroke,"#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]","") ~= "" then
            error("Incorrect hexidecimal format for argument stroke!") end
end

local function checkid(id)
       id = string.gsub(id,"q","Q")
       id = string.gsub(id,"%s+","")	    	
       if string.gsub(id,"^[Q]%d+$","") ~= "" then error("Bad format for parameter id!") end
   return id
end
-- GET LATITUDE

local function latitude(wikidata)
	local latitude = ""
	    local entity = mw.wikibase.getEntityObject(wikidata)	
	if entity == nil then error("Wikidata ID " .. wikidata .. " not found!") end
	local claims = entity.claims
	if claims == nil then error("Wikidata ID found No Data!") end	
	if claims.P625 ~= nil then
		latitude = entity.claims.P625[1].mainsnak.datavalue.value.latitude
		return latitude
	end
	if latitude == "" then error("Latitude not found in Wikidata!") end
		return latitude
	end

--  GET LONGITUDE -- P625

local function longitude(wikidata)
	local longitude = ""
	    local entity = mw.wikibase.getEntityObject(wikidata)	
	if entity == nil then error("Wikidata ID " .. wikidata .. " not found!") end	
	local claims = entity.claims
	if claims == nil then error("Wikidata ID found No Data!") end	
	if claims.P625 ~= nil then
		longitude = entity.claims.P625[1].mainsnak.datavalue.value.longitude
		return longitude
	end
	if longitude == "" then error("Longitude not found in Wikidata!") end
		return longitude
end


local function parts(lat,long,group,title,description,fill,stroke)
        local part1a = '<maplink class="no-icon" text="" latitude="' .. lat .. '" longitude="' .. long .. '" '
        local part1a = part1a .. 'zoom="5" group="' .. group .. '">\n{"type": "Feature","geometry":\t{"coordinates":\n'

        local part2a = '\n\t\t"type":"LineString"},\n\t"properties":{\n\t\t"title": "' .. title .. '",\n'
        local part2a = part2a .. '\t\t"description": "' .. description .. '",\n'
        local part2a = part2a .. '\t\t"stroke":"' .. stroke .. '",\n\t\t"stroke-width":1\n}}\n</maplink>\n'

        local part1b = '<maplink class="no-icon" text="" latitude="' .. lat .. '" longitude="' .. long .. '" '
        local part1b = part1b .. 'zoom="5" group="' .. group .. '">\n{"type": "Feature","geometry":\t{"coordinates":\n'

        local part2b = '\n\t\t"type":"Polygon"},\n\t"properties":{\n\t\t"title": "' .. title .. '",\n'
        local part2b = part2b .. '\t\t"description": "' .. description .. '",\n\t\t"fill": "' .. fill .. '",\n'
        local part2b = part2b .. '\t\t"stroke":"' .. stroke .. '",\n\t\t"stroke-width":1\n}}\n</maplink>\n'

        local part1c = '<mapframe text="" latitude="' .. lat .. '" longitude="' .. long .. '" '
        local part1c = part1c .. 'zoom="5" group="' .. group .. '" width="600" height="400" >\n{"type": "FeatureCollection",\n\t"features": [\n\t\t{\n\t\t"type": "Feature",\n\t\t"geometry": {"coordinates":\n'

        local part2ca = '\n\t\t"type":"LineString"},\n\t"properties":{\n\t\t"title": "' .. title .. '",\n'
        local part2ca = part2ca .. '\t\t"description": "' .. description .. '",\n\t\t"fill": "' .. fill .. '",\n'
        local part2ca = part2ca .. '\t\t"stroke":"' .. stroke .. '",\n\t\t"stroke-width":1\n}\n}]}\n</mapframe>\n'
        
        local part2cb = '\n\t\t"type":"Polygon"},\n\t"properties":{\n\t\t"title": "' .. title .. '",\n'
        local part2cb = part2cb .. '\t\t"description": "' .. description .. '",\n\t\t"fill": "' .. fill .. '",\n'
        local part2cb = part2cb .. '\t\t"stroke":"' .. stroke .. '",\n\t\t"stroke-width":1\n}\n}]}\n</mapframe>\n' 

        return part1a,part2a,part1b,part2b,part1c,part2ca,part2cb
end

-- CIRCLE

function p.circle(frame)
	   local shape = "circle"
	   local id = frame.args['id']	or ""
	   local lat,long = "",""
	   local x,y = 0,0
	   if id == nil or id == "" then
              if frame.args['lat'] == nil then error("Missing argument lat!") end
              if frame.args['long'] == nil then error("Missing argument long!") end
              lat = frame.args['lat']
              long = frame.args['long']
              if tonumber(frame.args['lat']) > 90 or tonumber(frame.args['lat']) < -90 then
           	   error("Latitude must be between 90 and -90!") end
              if tonumber(frame.args['long']) > 180 or tonumber(frame.args['long']) < -180 then
           	   error("Longitude must be between 180 and -180!") end
	    else
              id = checkid(id)
              lat = latitude(id)
              long = longitude(id)	
	   end        
           x = string.format("%.6f",lat)
           y = string.format("%.6f",long)                   
        
        local marker = frame.args['marker'] or "no"
              if marker == nil or marker == "" then marker = "no" end
        local mapframe = frame.args['mapframe'] or "no"
              if mapframe == nill or mapframe == "" then mapframe = "no" end -- FUTURE USE              
        if tonumber(lat) > 85.35 or tonumber(lat) < -85.35 then
        	error("Latitude must be between 85.35 and -85.35!") end -- END if
-- I set this as a default - function will not handle the polar circles yet will still handle areas within the majority of a map
        if tonumber(long) > 180 or tonumber(long) < -180 then
        	error("Longitude must be between 180 and -180!") end -- END if
-- this will draw a full circle at 0 lat and 180 long
        local group = frame.args['group'] or 'circle'
        local title = frame.args['title'] or 'A circle'
        local description = frame.args['desc'] or ''
        local r = frame.args['radius'] or ".5"   -- default
        -- radius of 10 is approx. 500 km - futz with sizes - below 3 would probably be adequate
        -- .1 is about 20km - .0001 is about 30 m
                r = tonumber(r)
                if r > 10 then error("10 for radius is MAX") end   -- END if - my default
                if r <= 0 then error("radius has to be greater than 0") end -- END if
        local fill = frame.args['fill'] or "#ccef64"
        local stroke = frame.args['stroke'] or "#0000ff"
        checkhex(fill,stroke)
        local data = {}
        local coordinates = ""
        local ptx,pty,angle = 0,0,0
        local type = frame.args['type'] or "line" -- default line for LineString
        if type ~= "line" then
                type = "poly"
        end -- END if

        local part1a,part2a,part1b,part2b,part1c,part2ca,part2cb = parts(lat,long,group,title,description,fill,stroke)

        if tonumber(x) >= 10.5 then
            x = math.log(math.tan((90 + x) * math.pi/360)) / (math.pi/180)
        elseif tonumber(x) <= -10.5 then
            x = math.log(math.tan((90 + x) * math.pi/360)) / (math.pi/180)
        end -- END if ELSEIF

        for i = 1, 360 do
           angle = i * math.pi / 180
           ptx = x + r * math.cos( angle )
           pty = y + r * math.sin( angle )
--         ptx, pty = x + r * math.cos( angle ), y + r * math.sin( angle ) -- original code split for readability above

        if tonumber(x) >= 10.5 then
         ptx = newlat(ptx) -- makes correction to make circle show up on map - upper latitudes
        end -- END if

        if tonumber(x) <= -10.5 then
          ptx = newlat(ptx) -- makes correction to make circle show up on map - lower latitudes
        end -- END if

          data[i] = '[' .. string.format("%.6f",pty) .. "," .. string.format("%.6f",ptx) .. ']'
        end -- END for

        for i = 5,359, 5 do
                data[i] = data[i] .. "@@@@@"
        end -- END for

-- cycle through array and build single string of all coordinates to be output

        for i = 1,360, 1 do
               coordinates = coordinates .. data[i]
        end -- END for

        coordinates = coordinates.gsub(coordinates,'%]%[','],[')
        coordinates = coordinates.gsub(coordinates,'%]@@@@@%[','],\n[')
        coordinates = "[" .. coordinates .. ',' .. data[1] .. "],"          -- close the circle extra precautionary measure

        if mapframe == "y" or mapframe == "yes" then
			if type == "poly" then
                coordinates  = string.gsub(coordinates,'%]%,$',']],')
                coordinates = part1c .. string.gsub(coordinates,'^%[','[[') .. part2cb
			else
                coordinates = part1c .. coordinates .. part2ca
			end -- END if
		else
			if type == "poly" then
                coordinates  = string.gsub(coordinates,'%]%,$',']],')
                coordinates = part1b .. string.gsub(coordinates,'^%[','[[') .. part2b
			else
                coordinates = part1a .. coordinates .. part2a
			end -- END if		
		
		end
        if marker == "yes" or marker == "y" then
              coordinates = coordinates .. '\n* {{marker|type=vicinity|name=Center Circle|lat=' .. lat .. "|long=" .. long .. '}}\n'
        end
        return coordinates

end



-- END MODULE


return p