Module
LinesOverlay
From Dogcraft Wiki
The {{LinesOverlay}} template uses Module:LinesOverlay to draw lines and place icons based on coordinates. This is intended for marking connections and facilities of the transport networks on the Dogcraft Server to be overlayed on a map. This module uses line by line markup for generating the lines and icons.
This template is intended to be used to create an overlay to be overlayed on a map, but can still be used for other purposes if so desired. The lines it creates are not particularly nice at sharp corners, as due to the limitations of Mediawiki lines are drawn with html <div> elements.
Usage
This template can generate a effectively unlimited number of lines and/or icons. It is important to note however, that this template only accepts a single parameter: a block of text of special markup.
Template opener
Open a LinesOverlay template with the template identifier: {{LinesOverlay|
Lines Overlay Markup
The markup is parsed line by line, meaning that every line of text given to the template acts as one parameter.
Please note that spaces ' ' and line breaks '\n' are significant in markup for this template and should only be used where needed.
Generally pieces of information are sperated by a single space ' ' in this templates markup and each line of markup acts like a parameter and provides a full set of information for one element. Every line begins with a line identifier and this is followed by information for this identifier.
This template accepts 3 types of information in markup: info, icon and line
Coordinate blocks
The template markup accepts many sets of coordinates, which are all specified as a coordinate block.
A coordinate block consists of a X and a Z coordinate separated by a comma ','.
For example the coordinates X:500, Z:300 translate to the following coordinate block: 500,300
The info line
info is an optional line that can be specified in markup. If used, this line should be the first under the template opener.
The info line can set a coordinate offset and a specific size and thus aspect ratio of the output.
The info line begins with the info
identifier, which is followed by 2 coordinate blocks.
The first coordinate block specifies offset coordinates for the output, which sets the top-left corner of the output to the given coordinates. The default for this is 0,0.
The second coordinate block specifies a custom size of the output. The x coordinate in this case specifies width and the z coordinate specifies height. The default for this is 500,500.
Example
info 500,-1000 1000,500
This will set the top-left corner of the output to be at coordinates X:500, Z:-1000 and will set the width of the output to be 1000 and the height to be 500, making the output twice as wide as it is high.
Icons
In addition to drawing lines, you can also place specific icons at specific coordinates with this template.
There are 5 predefined icons and you have the ability to specify a custom FontAwesome icon as well.
The 5 predefined icons are:
station
port
shelter
portal
hub
The predefined icons use the identifier shown above and accept a single coordinate block after the identifier. This coordinate block specifies the coordinates where the icon will be located.
A custom icon can be specified using the icon
identifier.
This identifier accepts 1 coordinate block and after that a unspecified number of css class names. This can be used with FontAwesome classes to specify an arbitrary icon.
The coordinate block, similar to the predefined icons, specifies the coordinates at which the icon will be located.
The css class names are inserted into the class attribute of a <i> html tag. If FontAwesome classes are used here, this can result in a FontAwesome icon being displayed.
Examples
Markup | Result |
---|---|
station 500,354 |
Located at coordinates X:500, Z:354 |
icon 500,354 fa-solid fa-door-open |
Located at coordinates X:500, Z:354 |
icon 845,-512 fa-solid fa-sailboat fa-house |
Located at coordinates X:845, Z:-512 1 |
icon 845,-512 fa-solid fa-sailboat green |
Located at coordinates X:845, Z:-512 2 |
icon 845,-512 fa-solid fa-sailboat otherclass |
Located at coordinates X:845, Z:-512 2 |
1 Note that multiple different icons overwrite each other and only one of the icon classes will have effect, thus only showing one icon.
2 Note that the class "otherclass" is not styled in any way on this page and thus has no effect. The class "green" sets the color to green, thus changing the color of the icon.
Lines
Lines use the identifier line
and accept first a color and width block and after that an arbitrary number of coordinate blocks.
The color and width block consists of a valid css color and a number for the line width (thickness) separated by a colon ':'.
Specifying the width is optional and defaults to 5.
CSS colors can be a word (eg "green"), a hex color (eg "#ff0000"), a rgb color (eg "rgb(255, 0, 0)"), or a rgba color (eg "rgba(255, 0, 0, 0.5)"). Please check the internet for examples or a list of available color names.
The following coordinate blocks are read left to right and the line is drawn in segments from point to point.
All coordinate blocks on the same line of text will be used to draw a single visible line.
To draw a line splitting to 2 lines, you can either specify them as 2 lines (the starting line continuing after the split as one of the two ends and the other end as a separate line) or as 3 lines (the starting line and 2 separate ends).
Lines can cross, overlap and coincide arbitrarily. Multiple lines can also have points at the exact same coordinates.
Examples
Markup | Result |
---|---|
line green 0,0 10,10 10,20 20,40 | |
line red 10,10 30,20 50,40 | |
line #0000ff60 10,10 30,20 40,40 30,40 30,10 |
Close Template
Dont forget to close the template below the last line of markup with }}!
Full example
Here is an example using most available features:
{{LinesOverlay| info 0,-500 station 258,-147 shelter 313,-154 port 271,-66 portal 272,-235 icon 323,-353 fa-solid fa-dog line #ff4500 237,-91 306,-91 324,-95 413,-95 434,-92 478,-72 514,-72 521,-60 line #ff4500 302,-91 305,-82 305,-66 308,-51 308,-25 327,8 line #ff4500 275,-92 274,-121 283,-134 284,-156 282,-168 269,-216 269,-231 274,-250 289,-262 297,-278 399,-379 399,-393 397,-396 385,-396 382,-399 382,-454 396,-468 396,-489 394,-491 387,-491 369,-509 line lime 269,-135 276,-132 328,-126 360,-100 422,-100 436,-97 468,-82 484,-83 489,-85 543,-85 line yellow 248,-139 244,-136 245,-130 253,-126 279,-126 304,-119 312,-111 315,-99 315,-56 318,-47 323,-43 328,-41 353,-41 361,-38 363,-34 365,-29 365,-13 367,-7 373,-2 383,0 389,6 }}
-- Copyright (c) 2024, XPModder, Dogcraft.net and contributors
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
-- to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
-- and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--
-- End license text.
-- A Module for generating lines and icons based on coordinates of each corner
local p = {}
local data = ""
local startX = 0
local startZ = 0
local width = 500
local height = 500
-- Each line of the input is one drawn line or one Icon on the overlay
-- Parses the line and generates HTML for it
local function parseLine(line)
line = mw.text.trim(line)
local type = ""
local output = ""
local continueIcon = 0
local width1Percent = width / 100
local height1Percent = height / 100
-- Split the line into comma separated key-pairs by spaces
local pairs = mw.text.split(line, " ", true)
for i = 1, #pairs do
-- Get the value
local value = pairs[i]
-- accepted parameters are line, station, shelter, port, portal and icon
if (i == 1) then
type = mw.text.trim(value)
else
-- type line takes a css color value as second argument and after that an arbitrary amount of coordinates
if (type == "line") then
local color = "green"
if (i == 2) then
local vals = mw.text.split(value, ":", true)
color = vals[1]
local lineWidth = tonumber(vals[2])
if (lineWidth == nil) then
lineWidth = 5
end
lineWidth = lineWidth / width1Percent
output = output .. "<div class='outerLinesContainer' style='--line-color: " .. color .. "; --line-width:" .. lineWidth .. "%;'>"
else
-- lines are drawn in pieces, each piece being drawn by starting at the current coordinates and drawing to the next coordinates
local nextValue = pairs[i+1]
-- if there is no set of coordinates after this one, we have reached the end of the line and close the outerLinesContainer before returning the content
if (nextValue == nil) then
output = output .. "</div>"
return output
end
-- get the coordinates
local startCoords = mw.text.split(value, ",", true)
local endCoords = mw.text.split(nextValue, ",", true)
local continue = 0
-- start x and z as well as end x and z are smaller then starting point
if (tonumber(startCoords[1]) < startX) then
if (tonumber(startCoords[2]) < startZ) then
if (tonumber(endCoords[1]) < startX) then
if (tonumber(endCoords[2]) < startZ) then
continue = 1
end
end
end
end
-- start x and end x are smaller then starting point
if (tonumber(startCoords[1]) < startX) then
if (tonumber(endCoords[1]) < startX) then
continue = 1
end
end
-- start z and end z are smaller then starting point
if (tonumber(startCoords[2]) < startZ) then
if (tonumber(endCoords[2]) < startZ) then
continue = 1
end
end
-- start x and z as well as end x and z are bigger then end point
if (tonumber(startCoords[1]) > (startX + width)) then
if (tonumber(startCoords[2]) > (startZ + height)) then
if (tonumber(endCoords[1]) > (startX + width)) then
if (tonumber(endCoords[2]) > (startZ + height)) then
continue = 1
end
end
end
end
-- start x and end x are bigger then ending point
if (tonumber(startCoords[1]) > (startX + width)) then
if (tonumber(endCoords[1]) > (startX + width)) then
continue = 1
end
end
-- start z and end z are bigger then ending point
if (tonumber(startCoords[2]) > (startZ + height)) then
if (tonumber(endCoords[2]) > (startZ + height)) then
continue = 1
end
end
-- if the entire line is outside the visible area, skip it
if (continue == 0) then
-- calculate the css values for the line segment
local top = tostring((tonumber(startCoords[2]) - startZ) / height1Percent) .. "%"
local left = tostring((tonumber(startCoords[1]) - startX) / width1Percent) .. "%"
local xdiff = tonumber(startCoords[1]) - tonumber(endCoords[1])
local zdiff = tonumber(endCoords[2]) - tonumber(startCoords[2])
-- protect against divide by 0
if (zdiff == 0) then
zdiff = 0.0000001
end
local angle = math.atan( xdiff / zdiff )
local transform = "rotate("
if (zdiff < 0) then
transform = transform .. "calc(" .. tostring(angle) .. "rad + 180deg)"
else
transform = transform .. tostring(angle) .. "rad"
end
transform = transform .. ")"
xdiff = tonumber(endCoords[1]) - tonumber(startCoords[1])
local height = tostring((math.sqrt( (xdiff ^ 2) + (zdiff ^ 2) )) / height1Percent) .. "%"
-- assemble all the data into one div for this line segment and append it to the output
output = output .. "<div class='line' style='top: " .. top .. "; left: " .. left .. "; height: " .. height .. "; transform: " .. transform .. ";'></div>"
end
end
-- stations only accept the coordinates of the station as second parameter, nothing else is parsed
elseif (type == "station") then
if (i == 2) then
local coords = mw.text.split(value, ",", true)
local continue = 0
if (tonumber(coords[1]) < startX) then
if (tonumber(coords[2]) < startZ) then
continue = 1
end
end
if (tonumber(coords[1]) > (startX + width)) then
if (tonumber(coords[2]) > (startZ + height)) then
continue = 1
end
end
if (continue == 0) then
local top = tostring((tonumber(coords[2]) - startZ) / height1Percent) .. "%"
local left = tostring((tonumber(coords[1]) - startX) / width1Percent) .. "%"
output = output .. "<div class='station' style='top: calc(" .. top .. " - 0.5em); left: calc(" .. left .. " - 0.5em);'>"
output = output .. "<i class='fa-solid fa-train'></i></div>"
return output
end
end
-- ports behave like stations, just with a different icon
elseif (type == "port") then
if (i == 2) then
local coords = mw.text.split(value, ",", true)
local continue = 0
if (tonumber(coords[1]) < startX) then
if (tonumber(coords[2]) < startZ) then
continue = 1
end
end
if (tonumber(coords[1]) > (startX + width)) then
if (tonumber(coords[2]) > (startZ + height)) then
continue = 1
end
end
if (continue == 0) then
local top = tostring((tonumber(coords[2]) - startZ) / height1Percent) .. "%"
local left = tostring((tonumber(coords[1]) - startX) / width1Percent) .. "%"
output = output .. "<div class='port' style='top: calc(" .. top .. " - 0.5em); left: calc(" .. left .. " - 0.5em);'>"
output = output .. "<i class='fa-solid fa-anchor'></i></div>"
return output
end
end
-- shelters are again like stations and ports, just with a different icon
elseif (type == "shelter") then
if (i == 2) then
local coords = mw.text.split(value, ",", true)
local continue = 0
if (tonumber(coords[1]) < startX) then
if (tonumber(coords[2]) < startZ) then
continue = 1
end
end
if (tonumber(coords[1]) > (startX + width)) then
if (tonumber(coords[2]) > (startZ + height)) then
continue = 1
end
end
if (continue == 0) then
local top = tostring((tonumber(coords[2]) - startZ) / height1Percent) .. "%"
local left = tostring((tonumber(coords[1]) - startX) / width1Percent) .. "%"
output = output .. "<div class='shelter' style='top: calc(" .. top .. " - 0.5em); left: calc(" .. left .. " - 0.5em);'>"
output = output .. "<i class='fa-solid fa-horse'></i></div>"
return output
end
end
-- portals are also like stations, ports and shelters, with a different icon
elseif (type == "portal") then
if (i == 2) then
local coords = mw.text.split(value, ",", true)
local continue = 0
if (tonumber(coords[1]) < startX) then
if (tonumber(coords[2]) < startZ) then
continue = 1
end
end
if (tonumber(coords[1]) > (startX + width)) then
if (tonumber(coords[2]) > (startZ + height)) then
continue = 1
end
end
if (continue == 0) then
local top = tostring((tonumber(coords[2]) - startZ) / height1Percent) .. "%"
local left = tostring((tonumber(coords[1]) - startX) / width1Percent) .. "%"
output = output .. "<div class='portal' style='top: calc(" .. top .. " - 0.5em); left: calc(" .. left .. " - 0.5em);'>"
output = output .. "<i class='fa-solid fa-dungeon'></i></div>"
return output
end
end
-- hubs are also like stations, ports and shelters, with a different icon
elseif (type == "hub") then
if (i == 2) then
local coords = mw.text.split(value, ",", true)
local continue = 0
if (tonumber(coords[1]) < startX) then
if (tonumber(coords[2]) < startZ) then
continue = 1
end
end
if (tonumber(coords[1]) > (startX + width)) then
if (tonumber(coords[2]) > (startZ + height)) then
continue = 1
end
end
if (continue == 0) then
local top = tostring((tonumber(coords[2]) - startZ) / height1Percent) .. "%"
local left = tostring((tonumber(coords[1]) - startX) / width1Percent) .. "%"
output = output .. "<div class='hub' style='top: calc(" .. top .. " - 0.5em); left: calc(" .. left .. " - 0.5em);'>"
output = output .. "<i class='fa-solid fa-compass'></i></div>"
return output
end
end
-- icon type accepts the coordinates as second parameter and the icon (fa class) as all further parameters
elseif (type == "icon") then
-- second parameter, get the coords and open the div and i
if (i == 2) then
local coords = mw.text.split(value, ",", true)
local continue = 0
continueIcon = 0
if (tonumber(coords[1]) < startX) then
if (tonumber(coords[2]) < startZ) then
continue = 1
continueIcon = 1
end
end
if (tonumber(coords[1]) > (startX + width)) then
if (tonumber(coords[2]) > (startZ + height)) then
continue = 1
continueIcon = 1
end
end
if (continue == 0) then
local top = tostring((tonumber(coords[2]) - startZ) / height1Percent) .. "%"
local left = tostring((tonumber(coords[1]) - startX) / width1Percent) .. "%"
output = output .. "<div class='icon' style='top: calc(" .. top .. " - 0.5em); left: calc(" .. left .. " - 0.5em);'>"
output = output .. "<i class='"
end
-- all further parameters after that, add parameter content to class field of i
else
if (continueIcon == 0) then
output = output .. value .. " "
-- when we are on the last parameter, close the i and div
if (pairs[i+1] == nil) then
output = output .. "'></i></div>"
return output
end
end
end
-- info type line should be the first line if used
-- its second parameter are x and z start coordinates for the entire overlay
-- the third parameter is the total width and height (defaults to 500 x 500)
-- can potentially be expanded to provide additional info
elseif (type == "info") then
if (i == 2) then
local coords = mw.text.split(value, ",", true)
startX = tonumber(coords[1])
startZ = tonumber(coords[2])
if (startX == nil) then
startX = 0
end
if (startZ == nil) then
startZ = 0
end
elseif (i == 3) then
local sizes = mw.text.split(value, ",", true)
width = tonumber(sizes[1])
height = tonumber(sizes[2])
if (width == nil) then
width = 500
end
if (height == nil) then
height = 500
end
end
-- comment line. dont do anything with it
elseif (type == "comment") then
end
end
end
return ""
end
-- Generates the html for provided coordinates
local function generate()
-- Trim data of whitespace
data = mw.text.trim(data)
-- Use mw.text.split to split the data into lines
local lines = mw.text.split(data, "\n")
-- Iterate through each line, calling parseLine on it and appending the string result of that to an output data
local output = ""
for _, line in ipairs(lines) do
output = output .. parseLine(line)
end
return "<div class='lines-overlay-container' style='max-width: " .. width .. "px; max-height: " .. height .. "px; aspect-ratio: " .. width .. "/" .. height .. ";'>" .. output .. "</div>"
end
-- The main function called on #invoke via the template
function p.linesOverlay(frame)
local args = {}
if frame == mw.getCurrentFrame() then
args = frame:getParent().args
else
args = frame
end
-- Iterate through each key pair in args
for key, value in pairs(args) do
-- Add to data
data = data .. value
end
return generate()
end
return p