Wikipedia
csbwiki
https://csb.wikipedia.org/wiki/Prz%C3%A9dn%C3%B4_starna
MediaWiki 1.47.0-wmf.9
first-letter
Media
Specjalnô
Diskùsëjô
Brëkòwnik
Diskùsëjô brëkòwnika
Wiki
Diskùsëjô Wiki
Òbrôzk
Diskùsëjô òbrôzków
MediaWiki
Diskùsëjô MediaWiki
Szablóna
Diskùsëjô Szablónë
Pòmòc
Diskùsëjô Pòmòcë
Kategòrëjô
Diskùsëjô Kategòrëji
TimedText
TimedText talk
Moduł
Dyskusja modułu
Wydarzenie
Dyskusja wydarzenia
Swòrë
0
4479
204454
67536
2026-07-05T14:46:05Z
Terrus38
14026
Usunięto przekierowanie do [[Swòrnégace]]
204454
wikitext
text/x-wiki
{{Wies infobox
| Wies=Swòrnégace
| rodzôcz_wsë=
| céch_wsë=
| ôrt_gminë=
| fana_wsë=
| kôrta_wsë=
| wòjewództwò=pòmòrsczé
| kréz=chònicczi
| gmina=Chònice
| szôłtëstwò=Swòrnégace
| miejskô=
| wiechrzëzna=
| gradëN=53
| minutëN=51
| sekùndëN=36
| gradëE=17
| minutëE=29
| sekùndëE=57
| wiżô=
| rok=2008
| lëdztwò=555
| gãscëzna=
| nr_czér=52
| pòcztowi_kòd=89-608
| reg_tôfla=GCH
| SIMC=
| szôłtës=
| www= http://www.swornegacie.org/
| òdjimk= Poland_Swornegacie_-_church.jpg
| galerëjô_commons= Category:Swornegacie
|}}
[[Òbrôzk:Swornegacie Kaszubski Dom Rękodzieła Ludowego 03.07.10 k.jpg|mały|left|Kaszëbsczé Dodóm Lëdowëch Rãcznëch Robòtów]]
'''Swòrë''' (pò pòlskù ''Swornegacie'') – kaszëbskô wies pòłożonô w [[Pòmòrsczé wòjewództwò|pòmòrsczim wòjewództwie]], w [[Chònicczi kréz|chònicczim krézu]], w gminie Chònice. Wies je nad [[Kôrsyńsczé Jezoro|Kôrsyńsczim Jezorã]]. Papiéż Grzegòrz X pisôł ò ti wsë w 1272 rokù. Tu béł zôkón Augùstianów. W 1291 ròkù béł w ti wnożënie ksyżã [[Mscëwòj II]] z trójną prowadzëną.
W Swòrnégacach w szkòle dzece ùczą sã [[Kaszëbsczi jãzëk|kaszëbsczégò jãzëka]].
== Lëteratura ==
* [http://www.ztn.com.pl/files_b/4_2009-lato-Ziemia%20Zaborska.pdf Przemysław Zientkowski, ''Swornegacie - stara kaszubska wieś'', „Ziemia Zaborska”, lato 2009, ss. 15 - 16.]
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XI/726 Swornigać w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
* [http://www.szkola-swornegacie.pl/ Szkòła]
{{Kòntrola}}
[[Kategòrëjô:Kaszëbë]]
[[Kategòrëjô:Pòmòrsczé wsë]]
[[Kategòrëjô:Gmina Chònice]]
q56bz6gnvxe7v1397pidepgn6rt2otp
204456
204454
2026-07-05T14:49:06Z
Terrus38
14026
kòrekta
204456
wikitext
text/x-wiki
{{Wies infobox
| Wies=Swòrë
| rodzôcz_wsë=Swòrów
| céch_wsë=
| ôrt_gminë=
| fana_wsë=
| kôrta_wsë=
| wòjewództwò=pòmòrsczé
| kréz=chònicczi
| gmina=Chònice
| szôłtëstwò=Swòrë
| miejskô=
| wiechrzëzna=
| gradëN=53
| minutëN=51
| sekùndëN=36
| gradëE=17
| minutëE=29
| sekùndëE=57
| wiżô=
| rok=2008
| lëdztwò=555
| gãscëzna=
| nr_czér=52
| pòcztowi_kòd=89-608
| reg_tôfla=GCH
| SIMC=
| szôłtës=
| www= http://www.swornegacie.org/
| òdjimk= Poland_Swornegacie_-_church.jpg
| galerëjô_commons= Category:Swornegacie
|}}
[[Òbrôzk:Swornegacie Kaszubski Dom Rękodzieła Ludowego 03.07.10 k.jpg|mały|left|Kaszëbsczé Dodóm Lëdowëch Rãcznëch Robòtów]]
'''Swòrë''' (pòl. ''Swornegacie'') – kaszëbskô wies pòłożonô w [[Pòmòrsczé wòjewództwò|pòmòrsczim wòjewództwie]], w [[Chònicczi kréz|chònicczim krézu]], w gminie Chònice. Leżi nad [[Kôrsyńsczé Jezoro|Kôrsyńsczim Jezorã]]. Wies òsta nadczidłô ju w 1272 rokù przez papieża Grégòra X. Tu dało zôkón [[Augùstianie|Augùstianów]]. W szkòle w Swòrach òdbiwają sã ùczbë [[Kaszëbsczi jãzëk|kaszëbsczégò jãzëka]].
== Lëteratura ==
* [http://www.ztn.com.pl/files_b/4_2009-lato-Ziemia%20Zaborska.pdf Przemysław Zientkowski, ''Swornegacie - stara kaszubska wieś'', „Ziemia Zaborska”, lato 2009, ss. 15 - 16.]
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XI/726 Swornigać w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
* [http://www.szkola-swornegacie.pl/ Szkòła]
{{Kòntrola}}
[[Kategòrëjô:Kaszëbë]]
[[Kategòrëjô:Pòmòrsczé wsë]]
[[Kategòrëjô:Gmina Chònice]]
a2q1w482ujl33eaza3wmrbfpftsaxbc
Lãbòrsczi kréz
0
4685
204512
199799
2026-07-06T11:57:21Z
Iketsi
3254
[[pòlsczi jãzëk|pòl.]]→{{jãz.|pl}}
204512
wikitext
text/x-wiki
[[Òbrôzk:POL powiat lęborski COA.svg|mały|Herb krézu]]
'''Lãbòrsczi kréz''' ({{jãz.|pl}} ''Powiat lęborski'') – je krezã w [[Pòmòrsczé wòjewództwò|pòmòrsczim wòjewództwie]] z sedzbą w [[Lãbòrg]]u.[[Norda|Norda.]] Wedle nôrodnégò spisënkù 2021, 1.438 lëdzy, w tim krézu, ùżiwô kaszëbsczégò jãzëka w dodomù.
=== Spòdlowé pòdôwczi ===
* Wiéchrzëzna: 706,99 km²
* Lëdztwò: 63 606 ([[2004]])
== Historiô ==
W [[Môj|maju]] 1958 rokù Służba Bezpiekù Pòwiatowi Kòmańdë [[Òbëwatelskô Milicjô|Òbëwatelsczi Milicje]] w Lãbòrgù ùdbała so rozprôcowiwac kaszëbsczëch i ùkrajińsczëch dzejôrzów.
== Bùtnowé lënczi ==
{{Commons}}
* [http://www.powiat-lebork.com/ Starna przédnictwa krézu]
{{Pòmòrsczé krézë}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Lãbòrsczi kréz| ]]
9eyjff489r3hr0g6jzy5x65thwi7snu
Ecser
0
4813
204506
204417
2026-07-06T11:27:51Z
Iketsi
3254
{{Ùzémk artikla}} {{Kòntrola}}
204506
wikitext
text/x-wiki
'''Ecser''' – [[wies]] w [[Madżarskô|Madżarsczi Repùblice]], pòłożóné w òbéńdze [[Bùdapeszt]]u, blëżnô pòrtu lotniczégò Ferihegy ë aùtodardżi M0 ë namòlnégò bana 120a (Budapest-Újszász-Szolnok). Bliscze jemù wsë są to Mglód, Vecsés, Gyömrő a Üllő. Lëdztwò gardu to 3272 mieszkańców.
Wies mô baro dłëgô historëjô. Z 1315 rokù pòchôdô pierszô zmiónka ò wsë, leno Ecser bëł tam ju òd 896, czedë najachôł jen nôrod madżarsczi.
== Galeriô ==
<center>
<gallery>
Òbrôzk:Ecseri 151.jpg|
Òbrôzk:Ecseri 154.jpg|
Òbrôzk:Széchenyi utca, kápolna, Ecseri 158.jpg|
Òbrôzk:Ecseri 171.jpg|
Òbrôzk:Szlovák Tájház, Ecseri 159.jpg|
Òbrôzk:Ecseri 156.jpg|
Òbrôzk:Ecseri 168.jpg|
Òbrôzk:Ecseri 175.jpg|
Òbrôzk:Ecseri 178.jpg|
Òbrôzk:Ecseri 180.jpg|
Òbrôzk:Ecseri 173.jpg|
Òbrôzk:Széchenyi utca, Ecseri 166.jpg|
</gallery>
</center>
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Madżarskô]]
o0brmj2y0w2ytz72as4y2ob1opc71m9
Swòrnégace
0
5160
204455
201126
2026-07-05T14:46:11Z
Terrus38
14026
Przeczérowanié do [[Swòrë]]
204455
wikitext
text/x-wiki
#PATRZ [[Swòrë]]
exxslxlgjbplu74sknz9uhm5frfw1l4
Barnim I
0
5925
204473
201216
2026-07-05T16:45:15Z
HaxelKaszëba
17492
cos jem ùprawił ale czedës cos dodóm wicy kò za krótczi artikel
204473
wikitext
text/x-wiki
[[Òbrôzk:BarnimI.Siegel.JPG|mały|]]
[[Òbrôzk:Cammin prince-bishopric 1250 (134037781).jpg|mały|Pòmòrsczé òkòlé kòl 1250 rokù]]
'''Barnim I''' (ùr. kòl 1217 rokù) – béł princã [[Zôpadnô Pòmòrskô|Zôpadnégò Pòmòrzégò]] do 1278 rokù. Jegò matką bëła [[Mirosława]]. Miôł òn sztãpel z nôpisã: ''Sigillum Barnym illustris ducis Slauorum et Cassubie'' (sztãpel Barnima ... princa [[Słowiónie|Słowiónów]] a [[Kaszëbskô|Kaszëbsczi]])[https://books.google.pl/books?id=bq0OAAAAYAAJ&pg=RA1-PA141&lpg=RA1-PA141&dq=cassubien&source=bl&ots=aQIzj8B7AF&sig=6u1WG9v9vBBJFqcbfxFSBEWWtjo&hl=pl&sa=X&ved=0ahUKEwj7o7X3pcjLAhVlOJoKHaJXD58Q6AEIPzAG#v=onepage&q=cassubien&f=false]. Pòstawił téż [[Marijën Kòscół w Szczecënie]].
== Lëteratura ==
* [[Gerard Labùda]]: Kaszubi i ich dzieje, Gdańsk 1996, s. 90–91 ISBN 83-904950-9-0 (pò pòlskù)
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Ksyżëce Zôpadny Pòmòrsczi]]
[[Kategòrëjô:Szczecëno]]
fzwf17nsao09hddpwl29kbk9k1m6jmd
Czerwònuszk
0
6153
204489
200361
2026-07-05T20:36:39Z
Rabùszniczka
19710
Nót je wiãcy dopisac
204489
wikitext
text/x-wiki
[[Òbrôzk:Erithacus rubecula -RHS Garden Harlow Carr-8b-2c.jpg|mały|Czerwònuszk]]
[[Òbrôzk:Erithacus rubecula whiterbyi Hartert, E, 1910 232 HdB Ouarsenis Algérie.jpg|mały|''Erithacus rubecula'']]
[[Òbrôzk:Cuculus canorus canorus MHNT.ZOO.2010.11.150.1.jpg|mały|''Cuculus canorus'' + ''Erithacus rubecula'']]
'''Czerwònuszk''' (''Erithacus rubecula'') — [[gatënk]] môłégò wanożnégò ptôcha z rodzëznë [[Muchòłówkaté|muchòłówka''tëch'']] ''(Muscicapidae), chòc wczesni zalëczóny do [[Drozdowaté|drozdowatëch]] (Turididae''). Jedurny ptôch w rodzaju ''Erithacus.'' Nie je zagrożóny.
== Wëstãpòwanié ==
Czerwònuszk je do nalézeniô w całi Eùropie, zôpadnym parce Syberii i Azji Mniszi. Pòza tim są izolowóné pòpùlacje lągòwé w [[Algieriô|Algerii]], czë na òstrowach wschòdnégi [[Atlantik|Atlantikù]]. Ni ma gò na [[Islandëjô|Islandii]], w nordowi [[Skandinawiô|Skandinawii]] i w niechtërnëch partach [[Ruskô|Ruska]]. Zëmùje w pôłniowi i zôpadny Eùropie (pòlskô pòpùlacjô dërch na Iberijsczim Półòstrowie), północny Africe, Azji Mniszi i na Blësczim Wschòdze.
W Pòlsce je to lëczny lągòwi ptôch, rozkòscérzóny w całim kraju. Pòdle szacënków [[Monitoringu Pospolitych Ptaków Lęgowyc|Monitoringu Pospolitych Ptaków Lęgowych]] w latach 2013-2018 pòpùlacjô pòlsczégò czerwònuszka rechòwała 2,20 - 2,98 milionów lągòwëch pôr<ref>Chodkiewicz T., Chylarecki P., Sikora A., Wardecki Ł., Bobrek R., Neubauer G., Marchowski D., Dmoch A., Kuczyński L.. [https://otop.org.pl/wp-content/uploads/2020/03/Biuletyn20_2019.pdf Raport z wdrażania art. 12 Dyrektywy Ptasiej w Polsce w latach 2013–2018: stan, zmiany, zagrożenia.] „Biuletyn Monitoringu Przyrody”. 20, s. 1–80, 2019.</ref>. Part ptôchów zëmùje na lągòwich terenach.
== Znanczi gatënkù ==
Czerwònuszk je môłim ptôcha ò wëlecałi i wëprostowany, abò krągłi i przësadzësti sylwetce. Jegò cało mierzi 12,5-14 cm, rozcygłosc lecadłów wënosy 20-25 cm, a ògón mô 5-6 cm długòscy. Ptôch ważi leno 14-21 gramów. Òbie płce są szlachòwnô òpierzóné i są do se pòdobné rozmiarã.
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Ptôchë]]
emk7r3yhqw5mmrirg0rylfed23yfwo4
204493
204489
2026-07-06T08:50:56Z
Rabùszniczka
19710
Dopisa jem wiãcy, do korektë. Wcyg brëkùje infòboksu na gatënczi.
204493
wikitext
text/x-wiki
[[Òbrôzk:Erithacus rubecula -RHS Garden Harlow Carr-8b-2c.jpg|mały|Czerwònuszk]]'''Czerwònuszk''' (''Erithacus rubecula'') — [[gatënk]] môłégò wanożnégò ptôcha z rodzëznë [[Muchòłówkaté|muchòłówka''tëch'']] ''(Muscicapidae), chòc wczesni zalëczóny do [[Drozdowaté|drozdowatëch]] (Turididae''). Jedurny ptôch w rodzaju ''Erithacus.'' Nie je zagrożóny.
== Systematika ==
Czerwònuszk razã z wieloma jinszëmi gatënkama òstôł fòrmalno òpsióny jak''ò Motacilla rubecula'' przez szwedzczégò rôdnika [[Karol Linneùsz|Karola Lineùsza]] w dzësąti edicji jegò przełomòwégò dlô biologii dokôzu z 1758 - ''[[Systema Naturae]].''<ref>[[Karol Linneùsz]]: [https://www.biodiversitylibrary.org/item/10277#page/208/mode/1up ''Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata.''] Holmiae. (Laurentii Salvii), 1758, s. 188. (łac.).</ref>
== Wëstãpòwanié ==
[[Òbrôzk:ErithacusRubeculaIUCN.svg|mały|Kôrta wëstãpòwaniô czerwònuszka. Jasnozelony - lągòwi cząd, cemnozelony - całorocznô pòpùlacjô, mòdri - zëmòwiszcza, różowi - introdukòwóny i prôwdopòdobno wëmarłi.]]
Czerwònuszk je do nalézeniô w całi Eùropie, zôpadnym parce Syberii i Azji Mniszi. Pòza tim są izolowóné pòpùlacje lągòwé w [[Algieriô|Algerii]], czë na òstrowach wschòdnégi [[Atlantik|Atlantikù]]. Ni ma gò na [[Islandëjô|Islandii]], w nordowi [[Skandinawiô|Skandinawii]] i w niechtërnëch partach [[Ruskô|Ruska]]. Zëmùje w pôłniowi i zôpadny Eùropie (pòlskô pòpùlacjô dërch na Iberijsczim Półòstrowie), północny Africe, Azji Mniszi i na Blësczim Wschòdze.
W Pòlsce je to lëczny lągòwi ptôch, rozkòscérzóny w całim kraju. Pòdle szacënków [[Monitoringu Pospolitych Ptaków Lęgowyc|Monitoringu Pospolitych Ptaków Lęgowych]] w latach 2013-2018 pòpùlacjô pòlsczégò czerwònuszka rechòwała 2,20 - 2,98 milionów lągòwëch pôr<ref>Chodkiewicz T., Chylarecki P., Sikora A., Wardecki Ł., Bobrek R., Neubauer G., Marchowski D., Dmoch A., Kuczyński L.. [https://otop.org.pl/wp-content/uploads/2020/03/Biuletyn20_2019.pdf Raport z wdrażania art. 12 Dyrektywy Ptasiej w Polsce w latach 2013–2018: stan, zmiany, zagrożenia.] „Biuletyn Monitoringu Przyrody”. 20, s. 1–80, 2019.</ref>. Part ptôchów zëmùje na lągòwich terenach.
== Znanczi gatënkù ==
[[Òbrôzk:Bold Robin (98857441).jpeg|mały|Charakteristicznô plama na piersë czerwònuszka.]]
=== Òpisenk pòzdrzatkù ===
Czerwònuszk je môłim ptôcha ò wëlecałi i wëprostowany, abò krągłi i przësadzësti sylwetce. Jegò cało mierzi 12,5-14 cm, rozcygłosc lecadłów wënosy 20-25 cm, a ògón mô 5-6 cm długòscy. Ptôch ważi leno 14-21 gramów. Òbie płce są szlachòwnô òpierzóné i są do se pòdobné rozmiarã. Czerwònuszczi mają charakteristiczné ùfarwienié - jakò dozdrzeniałé òsobniczi mają dërdzawą plamã, jakô rozcygô sã na piersë, gôrdzel i bòczi głowë, kòl ni je mòdrô-szari "kòntur". Plama ta z czasã widnie i staje sã matowô. Brzëch ptôcha je biôłi, a jegò bòczi, ògón i slôdk są szaro-brùné. Lëcadła są òliwkòwò-zeloné.
[[Òbrôzk:European Robin - Erithacus rubecula - Juvenile.jpg|mały|Czerwònuszk w juwelanym ùfarwieniu.]]
Młodé ptôchë w juwenalnym òpierzeniu są bruné z cëmnyma plamami i jasnijszim brzëchã. Dozdrzeniałé ùfarwienié zaczinô pòjawiac sã pò kòl półtora miesąca zëcô (czerwińc - séwnik), a pò rokù òsygô finalną fòrmã. <ref>Rob Hume i inni red., ''Ptaki Europy: przewodnik do rozpoznawania'', Warszawa: Multico Oficyna Wydawnicza, 2023, s. 392, [[:pl:Specjalna:Książki/9788377636084|ISBN 978-83-7763-608-4.]]</ref>
Dzãka dërdzawi plamie i jednorodny farwie lëcadłow czerwònusczi są dosc łatwé do rozpòznaniô w rôdnym strzôdwiszczu. [[Òbrôzk:Erithacus rubecula whiterbyi Hartert, E, 1910 232 HdB Ouarsenis Algérie.jpg|mały|''Erithacus rubecula'']]
== Lągòwi cząd ==
=== Jaje ===
Samica niese 5-6<ref>Michał Radziszewski i inni red., Ilustrowana encyklopedia ptaków Polski: przewodnik ornitologa, Bełchatów : Warszawa: Fenix ; PZWL Wydawnictwo Lekarskie Sp. z o.o., 2022, s. 264, [[:pl:Specjalna:Książki/9788365808486|ISBN 978-83-65808-48-6.]]</ref> jajów ò różnym ùfarwieniu (nôczãscy są to biôłe jaje z dërdzawëmi, czë zelonymi kropkama). W drëdżim lągù je jich mni - 4-5 sztëk. Téż nie kòżdô samica przëstãpùje do drëdżégò lągù.
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Ptôchë]]
<references />
[[Kategòrëjô:Mùchòłówczi]]
okixd854iqeajiqb2lcyvyc8fssy6el
204494
204493
2026-07-06T09:32:10Z
Orbitminis
18250
204494
wikitext
text/x-wiki
[[Òbrôzk:Erithacus rubecula -RHS Garden Harlow Carr-8b-2c.jpg|mały|Czerwònuszk]]'''Czerwònuszk''' (''Erithacus rubecula'') — [[gatënk]] môłégò wanożnégò ptôcha z rodzëznë [[Muchòłówkaté|muchòłówka''tëch'']] ''(Muscicapidae), chòc wczesni zalëczóny do [[Drozdowaté|drozdowatëch]] (Turididae''). Jedurny ptôch w rodzaju ''Erithacus.'' Nie je zagrożóny.
== Systematika ==
Czerwònuszk razã z wieloma jinszëmi gatënkama òstôł fòrmalno òpsióny jak''ò Motacilla rubecula'' przez szwedzczégò rôdnika [[Karol Linneùsz|Karola Lineùsza]] w dzësąti edicji jegò przełomòwégò dlô biologii dokôzu z 1758 - ''[[Systema Naturae]].''<ref>[[Karol Linneùsz]]: [https://www.biodiversitylibrary.org/item/10277#page/208/mode/1up ''Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata.''] Holmiae. (Laurentii Salvii), 1758, s. 188. (łac.).</ref>
== Wëstãpòwanié ==
[[Òbrôzk:ErithacusRubeculaIUCN.svg|mały|Kôrta wëstãpòwaniô czerwònuszka. Jasnozelony - lągòwi cząd, cemnozelony - całorocznô pòpùlacjô, mòdri - zëmòwiszcza, różowi - introdukòwóny i prôwdopòdobno wëmarłi.]]
Czerwònuszk je do nalézeniô w całi Eùropie, zôpadnym parce Syberii i Azji Mniszi. Pòza tim są izolowóné pòpùlacje lągòwé w [[Algieriô|Algerii]], czë na òstrowach wschòdnégi [[Atlantik|Atlantikù]]. Ni ma gò na [[Islandëjô|Islandii]], w nordowi [[Skandinawiô|Skandinawii]] i w niechtërnëch partach [[Ruskô|Ruska]]. Zëmùje w pôłniowi i zôpadny Eùropie (pòlskô pòpùlacjô dërch na Iberijsczim Półòstrowie), północny Africe, Azji Mniszi i na Blësczim Wschòdze.
W Pòlsce je to lëczny lągòwi ptôch, rozkòscérzóny w całim kraju. Pòdle szacënków [[Monitoringu Pospolitych Ptaków Lęgowyc|Monitoringu Pospolitych Ptaków Lęgowych]] w latach 2013-2018 pòpùlacjô pòlsczégò czerwònuszka rechòwała 2,20 - 2,98 milionów lągòwëch pôr<ref>Chodkiewicz T., Chylarecki P., Sikora A., Wardecki Ł., Bobrek R., Neubauer G., Marchowski D., Dmoch A., Kuczyński L.. [https://otop.org.pl/wp-content/uploads/2020/03/Biuletyn20_2019.pdf Raport z wdrażania art. 12 Dyrektywy Ptasiej w Polsce w latach 2013–2018: stan, zmiany, zagrożenia.] „Biuletyn Monitoringu Przyrody”. 20, s. 1–80, 2019.</ref>. Part ptôchów zëmùje na lągòwich terenach.
== Znanczi gatënkù ==
[[Òbrôzk:Bold Robin (98857441).jpeg|mały|Charakteristicznô plama na piersë czerwònuszka.]]
=== Òpisenk pòzdrzatkù ===
Czerwònuszk je môłim ptôcha ò wëlecałi i wëprostowany, abò krągłi i przësadzësti sylwetce. Jegò cało mierzi 12,5-14 cm, rozcygłosc lecadłów wënosy 20-25 cm, a ògón mô 5-6 cm długòscy. Ptôch ważi leno 14-21 gramów. Òbie płce są szlachòwnô òpierzóné i są do se pòdobné rozmiarã. Czerwònuszczi mają charakteristiczné ùfarwienié - jakò dozdrzeniałé òsobniczi mają dërdzawą plamã, jakô rozcygô sã na piersë, gôrdzel i bòczi głowë, kòl ni je mòdrô-szari "kòntur". Plama ta z czasã widnie i staje sã matowô. Brzëch ptôcha je biôłi, a jegò bòczi, ògón i slôdk są szaro-brùné. Lëcadła są òliwkòwò-zeloné.
[[Òbrôzk:European Robin - Erithacus rubecula - Juvenile.jpg|mały|Czerwònuszk w juwelanym ùfarwieniu.]]
Młodé ptôchë w juwenalnym òpierzeniu są bruné z cëmnyma plamami i jasnijszim brzëchã. Dozdrzeniałé ùfarwienié zaczinô pòjawiac sã pò kòl półtora miesąca zëcô (czerwińc - séwnik), a pò rokù òsygô finalną fòrmã. <ref>Rob Hume i inni red., ''Ptaki Europy: przewodnik do rozpoznawania'', Warszawa: Multico Oficyna Wydawnicza, 2023, s. 392, [[:pl:Specjalna:Książki/9788377636084|ISBN 978-83-7763-608-4.]]</ref>
Dzãka dërdzawi plamie i jednorodny farwie lëcadłow czerwònusczi są dosc łatwé do rozpòznaniô w rôdnym strzôdwiszczu. [[Òbrôzk:Erithacus rubecula whiterbyi Hartert, E, 1910 232 HdB Ouarsenis Algérie.jpg|mały|''Erithacus rubecula'']]
== Lągòwi cząd ==
=== Jaje ===
Samica niese 5-6<ref>Michał Radziszewski i inni red., Ilustrowana encyklopedia ptaków Polski: przewodnik ornitologa, Bełchatów : Warszawa: Fenix ; PZWL Wydawnictwo Lekarskie Sp. z o.o., 2022, s. 264, [[:pl:Specjalna:Książki/9788365808486|ISBN 978-83-65808-48-6.]]</ref> jajów ò różnym ùfarwieniu (nôczãscy są to biôłe jaje z dërdzawëmi, czë zelonymi kropkama). W drëdżim lągù je jich mni - 4-5 sztëk. Téż nie kòżdô samica przëstãpùje do drëdżégò lągù.
== Bùtnowé lënczi ==
{{Commons}}
== Przëpisë ==
[[Kategòrëjô:Ptôchë]]
<references />{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Mùchòłówczi]]
cfxsx0g9ndvexzhl06oesrwv34se42u
204495
204494
2026-07-06T10:30:37Z
Terrus38
14026
kòrekta
204495
wikitext
text/x-wiki
[[Òbrôzk:Erithacus rubecula -RHS Garden Harlow Carr-8b-2c.jpg|mały|Czerwònuszk]]'''Czerwònuszk''' (''Erithacus rubecula'') — [[gatënk]] môłégò wanożnégò ptôcha z rodzëznë [[Muchòłówkòwaté|muchòłówkòwatëch]] ''(Muscicapidae), chòc wczasni zaliczóny do [[Drozdowaté|drozdowatëch]] (Turididae''). Jedurny ptôch w rodzaju ''Erithacus.'' Nie je zagrożony.
== Systematika ==
Czerwònuszk razã z wiele jinszima gatënkama òstôł fòrmalno òpisóny jakò ''Motacilla rubecula'' przez szwedzczégò przërodnika [[Karol Linneùsz|Karola Linneùsza]] w dzësąti edicji jegò przełomòwégò dlô biologii dokazu z 1758 – ''[[Systema Naturae]].''<ref>[[Karol Linneùsz]]: [https://www.biodiversitylibrary.org/item/10277#page/208/mode/1up ''Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata.''] Holmiae. (Laurentii Salvii), 1758, s. 188. (łac.).</ref>
== Wëstãpòwanié ==
[[Òbrôzk:ErithacusRubeculaIUCN.svg|mały|Kôrta wëstãpòwaniô czerwònuszka. Jasnozelony – lãgòwi cząd, cemnozelony – całorocznô pòpùlacjô, mòdri – zëmòwiszcza, różewi – introdukòwóny i prôwdopòdobno wëmarłi.]]
Czerwònuszk je do nalézeniô wnet w całi [[Eùropa|Eùropie]], zôpadnym parce [[Syberiô|Syberii]] i Azji Mniészi. Pòza tim są izolowóné lãgòwé pòpùlacje w [[Algieriô|Algerii]] czë na òstrowach wschódnégò [[Atlantik|Atlantikù]]. Ni ma gò na [[Islandëjô|Islandii]], w nordowi [[Skandinawiô|Skandinawii]] i w niechtërnëch partach [[Ruskô|Rusczi]]. Zëmùje w pôłniowi i zôpadny Eùropie (pòlskô pòpùlacjô na Iberijsczim Półòstrowie), północny Africe, Azji Mniészi i na Blësczim Wschòdze.
W Pòlsce je to lëczny lãgòwi ptôch, rozkòscérzóny w całim kraju. Pòdle szacënków ''Monitoringu Pospolitych Ptaków Lęgowych'', w latach 2013-2018 pòpùlacjô pòlsczégò czerwònuszka rechòwała 2,20–2,98 milionów lãgòwëch pôr<ref>Chodkiewicz T., Chylarecki P., Sikora A., Wardecki Ł., Bobrek R., Neubauer G., Marchowski D., Dmoch A., Kuczyński L.. [https://otop.org.pl/wp-content/uploads/2020/03/Biuletyn20_2019.pdf Raport z wdrażania art. 12 Dyrektywy Ptasiej w Polsce w latach 2013–2018: stan, zmiany, zagrożenia.] „Biuletyn Monitoringu Przyrody”. 20, s. 1–80, 2019.</ref>. Dzél ptôchów zëmùje na lãgòwëch terenach.
== Znanczi gatënkù ==
[[Òbrôzk:Bold Robin (98857441).jpeg|mały|Charakteristicznô plama na piersë czerwònuszka.]]
=== Òpisënk wëzdrzatkù ===
Czerwònuszk je môłim ptôchã ò wëlecałi i wëprostowany, abò krãgłi i przësadzësti sylwetce. Jegò cało mierzi 12,5–14 cm, rozcygłosc lecëdłów wënosy 20-25 cm, a ògón mô 5-6 cm długòscy. Ptôch wôżi leno 14-21 gramów. Òbie pëłce są szlachòwno òpierzóné i są do se pòdobné rozmiarã. Czerwònuszczi mają charakteristiczné ùfarwienié - jakò dozdrzeniałé òsobniczi mają dërdzawą plamã, jakô rozcygô sã na piersë, gôrdzel i bòczi głowë, kòl ni je mòdro-szari "kòntur". Na plama z czasã widnieje i staje sã matowô. Brzëch ptôcha je biôłi, a jegò bòczi, ògón i slôdk są szaro-bruné. Lecëdła są òliwkòwò-zeloné.
[[Òbrôzk:European Robin - Erithacus rubecula - Juvenile.jpg|mały|Czerwònuszk w juwenalnym ùfarwieniu.]]
Młodé ptôchë w juwenalnym òpierzeniu są bruné z cemnyma plamama i jasniészim brzëchã. Dozdrzeniałé ùfarwienié zaczinô pòjawiac sã pò kòl półtora miesąca zëcô (czerwińc – séwnik), a pò rokù òsygô finalną fòrmã. <ref>Rob Hume i inni red., ''Ptaki Europy: przewodnik do rozpoznawania'', Warszawa: Multico Oficyna Wydawnicza, 2023, s. 392, [[:pl:Specjalna:Książki/9788377636084|ISBN 978-83-7763-608-4.]]</ref>
Dzãka dërdzawi plamie i jednorodny farwie lecëdłów czerwònusczi są dosc łôtwé do rozpòznaniô w nôtërnym strzodowiszczu. [[Òbrôzk:Erithacus rubecula whiterbyi Hartert, E, 1910 232 HdB Ouarsenis Algérie.jpg|mały|''Erithacus rubecula'']]
== Lãgòwi cząd ==
=== Jaja ===
Samica niese 5-6<ref>Michał Radziszewski i inni red., Ilustrowana encyklopedia ptaków Polski: przewodnik ornitologa, Bełchatów : Warszawa: Fenix ; PZWL Wydawnictwo Lekarskie Sp. z o.o., 2022, s. 264, [[:pl:Specjalna:Książki/9788365808486|ISBN 978-83-65808-48-6.]]</ref> jajów ò różnym ùfarwieniu (nôczãscy są to biôłé jaja z dërdzawima abò zelonyma kropkama). W drëdżim lãgù je jich mni – òd 4 do 5 sztëk. Nié kòżdô samica przëstãpùje do drëdżégò lãgù.
== Bùtnowé lënczi ==
{{Commons}}
== Przëpisë ==
[[Kategòrëjô:Ptôchë]]
<references />{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Mùchòłówczi]]
kj5qb8vx69ojyqkwbh25hvhcke787et
204500
204495
2026-07-06T11:19:23Z
Iketsi
3254
Przëpisë
204500
wikitext
text/x-wiki
[[Òbrôzk:Erithacus rubecula -RHS Garden Harlow Carr-8b-2c.jpg|mały|Czerwònuszk]]
'''Czerwònuszk''' (''Erithacus rubecula'') — [[gatënk]] môłégò wanożnégò ptôcha z rodzëznë [[Muchòłówkòwaté|muchòłówkòwatëch]] (''Muscicapidae''), chòc wczasni zaliczóny do [[Drozdowaté|drozdowatëch]] (''Turididae''). Jedurny ptôch w rodzaju ''Erithacus.'' Nie je zagrożony.
== Systematika ==
Czerwònuszk razã z wiele jinszima gatënkama òstôł fòrmalno òpisóny jakò ''Motacilla rubecula'' przez szwedzczégò przërodnika [[Karol Linneùsz|Karola Linneùsza]] w dzësąti edicji jegò przełomòwégò dlô biologii dokazu z 1758 – ''[[Systema Naturae]].''<ref>[[Karol Linneùsz]]: [https://www.biodiversitylibrary.org/item/10277#page/208/mode/1up ''Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata.''] Holmiae. (Laurentii Salvii), 1758, s. 188. (łac.).</ref>
== Wëstãpòwanié ==
[[Òbrôzk:ErithacusRubeculaIUCN.svg|mały|Kôrta wëstãpòwaniô czerwònuszka. Jasnozelony – lãgòwi cząd, cemnozelony – całorocznô pòpùlacjô, mòdri – zëmòwiszcza, różewi – introdukòwóny i prôwdopòdobno wëmarłi.]]
Czerwònuszk je do nalézeniô wnet w całi [[Eùropa|Eùropie]], zôpadnym parce [[Syberiô|Syberii]] i Azji Mniészi. Pòza tim są izolowóné lãgòwé pòpùlacje w [[Algieriô|Algerii]] czë na òstrowach wschódnégò [[Atlantik|Atlantikù]]. Ni ma gò na [[Islandëjô|Islandii]], w nordowi [[Skandinawiô|Skandinawii]] i w niechtërnëch partach [[Ruskô|Rusczi]]. Zëmùje w pôłniowi i zôpadny Eùropie (pòlskô pòpùlacjô na Iberijsczim Półòstrowie), północny Africe, Azji Mniészi i na Blësczim Wschòdze.
W Pòlsce je to lëczny lãgòwi ptôch, rozkòscérzóny w całim kraju. Pòdle szacënków ''Monitoringu Pospolitych Ptaków Lęgowych'', w latach 2013-2018 pòpùlacjô pòlsczégò czerwònuszka rechòwała 2,20–2,98 milionów lãgòwëch pôr<ref>Chodkiewicz T., Chylarecki P., Sikora A., Wardecki Ł., Bobrek R., Neubauer G., Marchowski D., Dmoch A., Kuczyński L.. [https://otop.org.pl/wp-content/uploads/2020/03/Biuletyn20_2019.pdf Raport z wdrażania art. 12 Dyrektywy Ptasiej w Polsce w latach 2013–2018: stan, zmiany, zagrożenia.] „Biuletyn Monitoringu Przyrody”. 20, s. 1–80, 2019.</ref>. Dzél ptôchów zëmùje na lãgòwëch terenach.
== Znanczi gatënkù ==
[[Òbrôzk:Bold Robin (98857441).jpeg|mały|Charakteristicznô plama na piersë czerwònuszka.]]
=== Òpisënk wëzdrzatkù ===
Czerwònuszk je môłim ptôchã ò wëlecałi i wëprostowany, abò krãgłi i przësadzësti sylwetce. Jegò cało mierzi 12,5–14 cm, rozcygłosc lecëdłów wënosy 20-25 cm, a ògón mô 5-6 cm długòscy. Ptôch wôżi leno 14-21 gramów. Òbie pëłce są szlachòwno òpierzóné i są do se pòdobné rozmiarã. Czerwònuszczi mają charakteristiczné ùfarwienié - jakò dozdrzeniałé òsobniczi mają dërdzawą plamã, jakô rozcygô sã na piersë, gôrdzel i bòczi głowë, kòl ni je mòdro-szari "kòntur". Na plama z czasã widnieje i staje sã matowô. Brzëch ptôcha je biôłi, a jegò bòczi, ògón i slôdk są szaro-bruné. Lecëdła są òliwkòwò-zeloné.
[[Òbrôzk:European Robin - Erithacus rubecula - Juvenile.jpg|mały|Czerwònuszk w juwenalnym ùfarwieniu.]]
Młodé ptôchë w juwenalnym òpierzeniu są bruné z cemnyma plamama i jasniészim brzëchã. Dozdrzeniałé ùfarwienié zaczinô pòjawiac sã pò kòl półtora miesąca zëcô (czerwińc – séwnik), a pò rokù òsygô finalną fòrmã. <ref>Rob Hume i inni red., ''Ptaki Europy: przewodnik do rozpoznawania'', Warszawa: Multico Oficyna Wydawnicza, 2023, s. 392, [[:pl:Specjalna:Książki/9788377636084|ISBN 978-83-7763-608-4.]]</ref>
Dzãka dërdzawi plamie i jednorodny farwie lecëdłów czerwònusczi są dosc łôtwé do rozpòznaniô w nôtërnym strzodowiszczu. [[Òbrôzk:Erithacus rubecula whiterbyi Hartert, E, 1910 232 HdB Ouarsenis Algérie.jpg|mały|''Erithacus rubecula'']]
== Lãgòwi cząd ==
=== Jaja ===
Samica niese 5-6<ref>Michał Radziszewski i inni red., Ilustrowana encyklopedia ptaków Polski: przewodnik ornitologa, Bełchatów : Warszawa: Fenix ; PZWL Wydawnictwo Lekarskie Sp. z o.o., 2022, s. 264, [[:pl:Specjalna:Książki/9788365808486|ISBN 978-83-65808-48-6.]]</ref> jajów ò różnym ùfarwieniu (nôczãscy są to biôłé jaja z dërdzawima abò zelonyma kropkama). W drëdżim lãgù je jich mni – òd 4 do 5 sztëk. Nié kòżdô samica przëstãpùje do drëdżégò lãgù.
== Bùtnowé lënczi ==
{{Commons}}
== Przëpisë ==
{{Przëpisë}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Ptôchë]]
[[Kategòrëjô:Mùchòłówczi]]
cvfyou4w6grzdk3izd27bqnvek68jgc
Złotowò
0
6853
204507
193896
2026-07-06T11:29:12Z
Terrus38
14026
kòrekta
204507
wikitext
text/x-wiki
{{gard-infobox
| Gard = Złotowò | adjektiw_mask = złotowsczi | adjektiw_fem = złotowskô
| céch = POL Złotów COA.svg | fana = POL Złotów flag.svg | karta =
| wòjewództwò = wiôlgòpòlsczé
| kréz = złotowsczi kréz | grodzki =
| wiéchrzëzna = 11,58
| stopniN = 53 | minutN = 21 | stopniE = 17 | minutE = 02
| rok = 2013
| lëdztwò = 18 712
| czerënkòwi numer = (+48) 67
| pòcztowi kòd = 77-400
| registracëjné tôfle = PZL
| bùrméster = Jakub Pieniążkowski
| adres_um = Urząd Miasta Złotowa, al. Piasta 1
| kod_poczt_um = 77-400 Złotów
| tel_um = +48 67 263 26 40
| fax_um = +48 67 265 00 25
| mail_um = urzad@zlotow.pl
| www = http://zlotow.pl
|gwôsné miono=Złotów|dopełniacz=Złotowa}}
'''Złotowò''' (we zdrojach: ''Vulatovum'' 1370, ''Welatowo'', ''Zlothaue'', ''Majus Zlothkowo'' XV w., ''Slothowo'' 1491, ''Plotho'' XVIII w., ''Flatow'', ''Złotowo''; ''Złetowo'' (Cenôwa); [[Pòlsczi jãzëk|pòl.]] ''Złotów'', [[Miemiecczi jãzëk|miem.]] ''Flatow'') je [[kréz]]ewim [[gard|gardã]] we [[wiôlgòpòlsczé wòjewództwò|wiôlgòpòlsczim wòjewództwie]] nad [[Głomia|Głomią]] ë piãc jezorama. Je jednym òd nôwikszich gardów [[Krajnô|Krôjnë]]. W 2013 rokù miało 18 712 mieszkańców, a jegò pòwiéchrzniô wënosy 11,58 km<sup>2</sup>. Òd [[1772]] ([[pierszi rozbiér Pòlsczi]]) do [[1945]] (kùńc [[drëgô swiatowô wòjna|drëdżi swiatowi wòjnë]]) słëchôł do [[Miemieckô|Miemiecczi]], dze béł wôżnym òstrzódkã [[Pòlaczi|pòlsczi miészëznë]]. W latach [[1938]]-[[1945|45]] leżôł w [[Pòmrë|Pòmrach]], [[1945]]-[[1946|46]] w [[Bëdgòsczé wòjewództwò (1945-1946)|bëdgòsczim wòjewództwie]], w [[1946]]-[[1950|50]] w [[Szczecyńsczé wòjewództwò (1946-1950)|szczecyńsczim]], w [[1950]]-[[1975|75]] w [[Kòszalińsczé wòjewództwò (1950-1975)|kòszalińsczim]], w [[1975]]-[[1998|98]] w [[Pilsczé wòjewództwò (1975-1998)|pilsczim]].
Złotowò leżi nad jezorama: [[Złotowsczé Jezoro|Złotowsczim]] (Miesczim), [[Jezoro Baba|Babą]], Bùrméstrowsczim, Probòszczowsczim, [[Zalesczé Jezoro|Zalesczim]] ë rzéką [[Głomia|Głomią]], we westrzédnozôpadnym parce Krôjnë.
{{stub}}
[[Kategòrëjô:Pòmòrsczé gardë]]
[[Kategòrëjô:Gardë w Pòlsce]]
[[Kategòrëjô:Wiôlgòpòlsczé wòjewództwò]]
6acjtepdfxvb9t97j087snwubx46x61
Lila Downs
0
7143
204511
189385
2026-07-06T11:56:34Z
Iketsi
3254
kat.
204511
wikitext
text/x-wiki
[[Òbrôzk:Lila Downs in Istanbul.jpg|mały|Lila Downs]]
'''Ana Lila Downs Sánchez''' (ùr. [[9 séwnika]] [[1968]], [[Oakzàca]], [[Meksyk]]) je Meksykanką i spiéwôrką.
{{Ùzémk artikla}}
{{Kòntrola}}
{{SORTUJ:Downs, Lila}}
[[Kategòrëjô:Spiéwôrczi]]
[[Kategòrëjô:Meksyk]]
56esdcq0067o30i0ahzvtxlljq1ax5s
Zeleniô
0
8446
204475
198303
2026-07-05T18:58:19Z
Terrus38
14026
204475
wikitext
text/x-wiki
{{WYŚWIETLANYTYTUŁ:Zeleniô}}
'''Zeleniô''' – [[Kaszëbë|kaszëbskô]] wies w [[gmina Kòscérzna|gminie Kòscérzna]], w [[Kòscersczi kréz|kòscersczim krézu]] [[Pòmòrsczé wòjewództwò|pòmòrsczégò wòjewództwa]]. Òd 1890 rokù tu bëła dwalatnô Gbùrskô Szkòła.
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XIV/593 ''Zielenina'' w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
[[Kategòrëjô:Gmina Kòscérzna]]
[[Kategòrëjô:Kaszëbë]]
h3f37lrqzc23dhdqiqpkex3fmlk1cjo
204476
204475
2026-07-05T18:59:36Z
Terrus38
14026
Terrus38 przeniós starnã [[Zelenin]] do [[Zeleniô]]: Błędna pisownia tytułu: Zeleniô je zgòdą z http://skarbnicakaszubska.pl/uchwaa-nr-6-2010/
204475
wikitext
text/x-wiki
{{WYŚWIETLANYTYTUŁ:Zeleniô}}
'''Zeleniô''' – [[Kaszëbë|kaszëbskô]] wies w [[gmina Kòscérzna|gminie Kòscérzna]], w [[Kòscersczi kréz|kòscersczim krézu]] [[Pòmòrsczé wòjewództwò|pòmòrsczégò wòjewództwa]]. Òd 1890 rokù tu bëła dwalatnô Gbùrskô Szkòła.
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XIV/593 ''Zielenina'' w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
[[Kategòrëjô:Gmina Kòscérzna]]
[[Kategòrëjô:Kaszëbë]]
h3f37lrqzc23dhdqiqpkex3fmlk1cjo
204479
204476
2026-07-05T19:15:43Z
Terrus38
14026
rozwicé artikla
204479
wikitext
text/x-wiki
{{WYŚWIETLANYTYTUŁ:Zeleniô}}{{Wies infobox|Wies=Zeleniô|rodzôcz_wsë=Zelenie|wòjewództwò=pòmòrsczé|kréz=kòscérsczi|gmina=Kòscérzna|szôłtëstwò=Zeleniô|lëdztwò=196|rok=2021|pòcztowi_kòd=83-422}}
'''Zeleniô''' (pòl. ''Zielenin'', niem. ''Zelenin'', òb lata 1942-1945 ''Altehütte'')<ref>wëbiér zdrzódłów, M. Kubicki ''Germanizacja nazw miejscowości w okręgu Rzeszy Gdańsk-Prusy Zachodnie 1939-1945'' (pòl.), Instytut Pamięci Narodowej, Gduńsk-Warszawa, 2022</ref> – [[Kaszëbë|kaszëbskô]] wies leżącô na [[Kaszëbsczé Pòjezerzé|Kaszëbsczim Pòjezerzim]], nad rzéką [[Wierzëca|Wierzëcą]] w [[Pòmòrsczé wòjewództwò|pòmòrsczim wòjewództwie]], w [[Kòscérsczi kréz|kòscérsczim krézu]], w [[Gmina Kòscérzna|gminie Kòscérzna]]. Wies je sedzbą szôłtëstwa Zeleniô.
W 2021 rokù w Zelenie mieszkało 196 òsób.<ref>Polska w Liczbach, ''[https://www.polskawliczbach.pl/wies_Zielenin Wieś Zielenin w liczbach]'' (pòl.), przëstãp z 5.07.2026 r.</ref>
W 1890 rokù pòwsta tuwò dwalatnô Gbùrskô Szkòła.
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XIV/593 ''Zielenina'' w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
[[Kategòrëjô:Gmina Kòscérzna]]
[[Kategòrëjô:Kaszëbë]]
9azju7yc52znmavtk6lf6v0d3n6du4s
204480
204479
2026-07-05T19:15:59Z
Terrus38
14026
204480
wikitext
text/x-wiki
{{WYŚWIETLANYTYTUŁ:Zeleniô}}{{Wies infobox|Wies=Zeleniô|rodzôcz_wsë=Zelenie|wòjewództwò=pòmòrsczé|kréz=kòscérsczi|gmina=Kòscérzna|szôłtëstwò=Zeleniô|lëdztwò=196|rok=2021|pòcztowi_kòd=83-422}}
'''Zeleniô''' (pòl. ''Zielenin'', niem. ''Zelenin'', òb lata 1942-1945 ''Altehütte'')<ref>wëbiér zdrzódłów, M. Kubicki ''Germanizacja nazw miejscowości w okręgu Rzeszy Gdańsk-Prusy Zachodnie 1939-1945'' (pòl.), Instytut Pamięci Narodowej, Gduńsk-Warszawa, 2022</ref> – [[Kaszëbë|kaszëbskô]] wies leżącô na [[Kaszëbsczé Pòjezerzé|Kaszëbsczim Pòjezerzim]], nad rzéką [[Wierzëca|Wierzëcą]] w [[Pòmòrsczé wòjewództwò|pòmòrsczim wòjewództwie]], w [[Kòscérsczi kréz|kòscérsczim krézu]], w [[Gmina Kòscérzna|gminie Kòscérzna]]. Wies je sedzbą szôłtëstwa Zeleniô.
W 2021 rokù w Zelenie mieszkało 196 òsób.<ref>Polska w Liczbach, ''[https://www.polskawliczbach.pl/wies_Zielenin Wieś Zielenin w liczbach]'' (pòl.), przëstãp z 5.07.2026 r.</ref>
W 1890 rokù pòwsta tuwò dwalatnô Gbùrskô Szkòła.
== Bùtnowé lënczi ==
{{Commons}}
== Przëpisë ==
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XIV/593 ''Zielenina'' w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
[[Kategòrëjô:Gmina Kòscérzna]]
[[Kategòrëjô:Kaszëbë]]
29dabsj0pf92wr6chm4z6arqyjxz4vc
204484
204480
2026-07-05T19:52:59Z
Orbitminis
18250
w historicznym pòrządkù, òdj., 1975-1998 woj.
204484
wikitext
text/x-wiki
{{WYŚWIETLANYTYTUŁ:Zeleniô}}{{Wies infobox|Wies=Zeleniô|rodzôcz_wsë=Zelenie|wòjewództwò=pòmòrsczé|kréz=kòscérsczi|gmina=Kòscérzna|szôłtëstwò=Zeleniô|lëdztwò=196|rok=2021|pòcztowi_kòd=83-422|gradëN=54|minutëN=07|sekùndëN=22|gradëE=18|sekùndëE=17|minutëE=05|galerëjô_commons=Category:Zielenin|òdjimk=Ciconia_ciconia_nest_Zielenin.jpg|reg_tôfla=GKS|SIMC=0164931}}
'''Zeleniô''' ([[Pòlsczi jãzëk|pòl]]. ''Zielenin'', [[Niemiecczi jãzëk|niem]]. ''Zelenin'', òb lata 1942-1945 ''Altehütte'')<ref>wëbiér zdrzódłów, M. Kubicki ''Germanizacja nazw miejscowości w okręgu Rzeszy Gdańsk-Prusy Zachodnie 1939-1945'' (pòl.), Instytut Pamięci Narodowej, Gduńsk-Warszawa, 2022</ref> – [[Kaszëbë|kaszëbskô]] wies leżącô na [[Kaszëbsczé Pòjezerzé|Kaszëbsczim Pòjezerzim]], nad rzéką [[Wierzëca|Wierzëcą]] przë [[Wòjewódzkô darga|wòjewódzczi dardze]] nr [[Òbrôzk:Tabliczka DW221.svg|bezramki|18x18px]] w [[Pòmòrsczé wòjewództwò|pòmòrsczim wòjewództwie]], w [[Kòscérsczi kréz|kòscérsczim krézu]], w [[Gmina Kòscérzna|gminie Kòscérzna]]. Wies je sedzbą szôłtëstwa Zeleniô.
W 1890 rokù pòwsta tuwò dwalatnô Gbùrskô Szkòła.
W latach 1975–1998 môlëzna administracyjno pòłożonô bëła w [[Gduńsczé wòjewództwò|gduńsczim wòjewództwie]].
W 2021 rokù w Zelenie mieszkało 196 òsób<ref>Polska w Liczbach, ''[https://www.polskawliczbach.pl/wies_Zielenin Wieś Zielenin w liczbach]'' (pòl.), przëstãp z 5.07.2026 r.</ref>.
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XIV/593 ''Zielenina'' w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
== Przëpisë ==
[[Kategòrëjô:Gmina Kòscérzna]]
[[Kategòrëjô:Kaszëbë]]
cyb4ve4ikt2t5h69xn1hnjt507yb54r
204488
204484
2026-07-05T20:27:20Z
Terrus38
14026
204488
wikitext
text/x-wiki
{{WYŚWIETLANYTYTUŁ:Zeleniô}}{{Wies infobox|Wies=Zeleniô|rodzôcz_wsë=Zelenie|wòjewództwò=pòmòrsczé|kréz=kòscérsczi|gmina=Kòscérzna|szôłtëstwò=Zeleniô|lëdztwò=196|rok=2021|pòcztowi_kòd=83-422|gradëN=54|minutëN=07|sekùndëN=22|gradëE=18|sekùndëE=17|minutëE=05|galerëjô_commons=Category:Zielenin|òdjimk=Ciconia_ciconia_nest_Zielenin.jpg|reg_tôfla=GKS|SIMC=0164931}}
'''Zeleniô''' ([[Pòlsczi jãzëk|pòl]]. ''Zielenin'', [[Niemiecczi jãzëk|niem]]. ''Zelenin'', òb lata 1942-1945 ''Altehütte'')<ref>wëbiér zdrzódłów, M. Kubicki ''Germanizacja nazw miejscowości w okręgu Rzeszy Gdańsk-Prusy Zachodnie 1939-1945'' (pòl.), Instytut Pamięci Narodowej, Gduńsk-Warszawa, 2022</ref> – [[Kaszëbë|kaszëbskô]] wies leżącô na [[Kaszëbsczé Pòjezerzé|Kaszëbsczim Pòjezerzim]], nad rzéką [[Wierzëca|Wierzëcą]] przë [[Wòjewódzkô droga|wòjewódzczi drodze]] nr [[Òbrôzk:Tabliczka DW221.svg|bezramki|18x18px]] w [[Pòmòrsczé wòjewództwò|pòmòrsczim wòjewództwie]], w [[Kòscérsczi kréz|kòscérsczim krézu]], w [[Gmina Kòscérzna|gminie Kòscérzna]]. Wies je sedzbą szôłtëstwa Zeleniô.
W 1890 rokù pòwsta tuwò dwalatnô Gbùrskô Szkòła.
W latach 1975–1998 môlëzna administracyjno pòłożonô bëła w [[Gduńsczé wòjewództwò|gduńsczim wòjewództwie]].
W 2021 rokù w Zelenie mieszkało 196 òsób<ref>Polska w Liczbach, ''[https://www.polskawliczbach.pl/wies_Zielenin Wieś Zielenin w liczbach]'' (pòl.), przëstãp z 5.07.2026 r.</ref>.
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XIV/593 ''Zielenina'' w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
== Przëpisë ==
[[Kategòrëjô:Gmina Kòscérzna]]
[[Kategòrëjô:Kaszëbë]]
basg0hyfbn3l38ruerwdv0ncw5j4ijv
204501
204488
2026-07-06T11:21:26Z
Iketsi
3254
[[Pòlsczi jãzëk|pòl]].→{{jãz.|pl}}; [[Niemiecczi jãzëk|niem]].→{{jãz.|de}}
204501
wikitext
text/x-wiki
{{Wies infobox|Wies=Zeleniô|rodzôcz_wsë=Zelenie|wòjewództwò=pòmòrsczé|kréz=kòscérsczi|gmina=Kòscérzna|szôłtëstwò=Zeleniô|lëdztwò=196|rok=2021|pòcztowi_kòd=83-422|gradëN=54|minutëN=07|sekùndëN=22|gradëE=18|sekùndëE=17|minutëE=05|galerëjô_commons=Category:Zielenin|òdjimk=Ciconia_ciconia_nest_Zielenin.jpg|reg_tôfla=GKS|SIMC=0164931}}
'''Zeleniô''' ({{jãz.|pl}} ''Zielenin'', {{jãz.|de}} ''Zelenin'', òb lata 1942–1945 ''Altehütte'')<ref>wëbiér zdrzódłów, M. Kubicki ''Germanizacja nazw miejscowości w okręgu Rzeszy Gdańsk-Prusy Zachodnie 1939–1945'' ({{jãz.|pl}}), Instytut Pamięci Narodowej, Gduńsk-Warszawa, 2022</ref> – [[Kaszëbë|kaszëbskô]] wies leżącô na [[Kaszëbsczé Pòjezerzé|Kaszëbsczim Pòjezerzim]], nad rzéką [[Wierzëca|Wierzëcą]] przë [[Wòjewódzkô droga|wòjewódzczi drodze]] nr [[Òbrôzk:Tabliczka DW221.svg|bezramki|18x18px]] w [[Pòmòrsczé wòjewództwò|pòmòrsczim wòjewództwie]], w [[Kòscérsczi kréz|kòscérsczim krézu]], w [[Gmina Kòscérzna|gminie Kòscérzna]]. Wies je sedzbą szôłtëstwa Zeleniô.
W 1890 rokù pòwsta tuwò dwalatnô Gbùrskô Szkòła.
W latach 1975–1998 môlëzna administracyjno pòłożonô bëła w [[Gduńsczé wòjewództwò|gduńsczim wòjewództwie]].
W 2021 rokù w Zelenie mieszkało 196 òsób<ref>Polska w Liczbach, ''[https://www.polskawliczbach.pl/wies_Zielenin Wieś Zielenin w liczbach]'' ({{jãz.|pl}}), przëstãp z 5.07.2026 r.</ref>.
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XIV/593 ''Zielenina'' w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
== Przëpisë ==
{{Przëpisë}}
{{Kòntrola}}
[[Kategòrëjô:Gmina Kòscérzna]]
[[Kategòrëjô:Kaszëbë]]
sikjzg9en89x77sq4q37zymhy459ylt
204505
204501
2026-07-06T11:25:56Z
Iketsi
3254
Bùtnowé lënczi
204505
wikitext
text/x-wiki
{{Wies infobox|Wies=Zeleniô|rodzôcz_wsë=Zelenie|wòjewództwò=pòmòrsczé|kréz=kòscérsczi|gmina=Kòscérzna|szôłtëstwò=Zeleniô|lëdztwò=196|rok=2021|pòcztowi_kòd=83-422|gradëN=54|minutëN=07|sekùndëN=22|gradëE=18|sekùndëE=17|minutëE=05|galerëjô_commons=Category:Zielenin|òdjimk=Ciconia_ciconia_nest_Zielenin.jpg|reg_tôfla=GKS|SIMC=0164931}}
'''Zeleniô''' ({{jãz.|pl}} ''Zielenin'', {{jãz.|de}} ''Zelenin'', òb lata 1942–1945 ''Altehütte'')<ref>wëbiér zdrzódłów, M. Kubicki ''Germanizacja nazw miejscowości w okręgu Rzeszy Gdańsk-Prusy Zachodnie 1939–1945'' ({{jãz.|pl}}), Instytut Pamięci Narodowej, Gduńsk-Warszawa, 2022</ref> – [[Kaszëbë|kaszëbskô]] wies leżącô na [[Kaszëbsczé Pòjezerzé|Kaszëbsczim Pòjezerzim]], nad rzéką [[Wierzëca|Wierzëcą]] przë [[Wòjewódzkô droga|wòjewódzczi drodze]] nr [[Òbrôzk:Tabliczka DW221.svg|bezramki|18x18px]] w [[Pòmòrsczé wòjewództwò|pòmòrsczim wòjewództwie]], w [[Kòscérsczi kréz|kòscérsczim krézu]], w [[Gmina Kòscérzna|gminie Kòscérzna]]. Wies je sedzbą szôłtëstwa Zeleniô.
W 1890 rokù pòwsta tuwò dwalatnô Gbùrskô Szkòła.
W latach 1975–1998 môlëzna administracyjno pòłożonô bëła w [[Gduńsczé wòjewództwò|gduńsczim wòjewództwie]].
W 2021 rokù w Zelenie mieszkało 196 òsób<ref>Polska w Liczbach, ''[https://www.polskawliczbach.pl/wies_Zielenin Wieś Zielenin w liczbach]'' ({{jãz.|pl}}), przëstãp z 5.07.2026 r.</ref>.
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_XIV/593 ''Zielenina'' w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
{{Kòntrola}}
[[Kategòrëjô:Gmina Kòscérzna]]
[[Kategòrëjô:Kaszëbë]]
bo8jz7wa6391awb70nr13h3chknbbfs
Lësatô kania
0
9406
204478
203353
2026-07-05T19:07:37Z
Rabùszniczka
19710
Napisa jem prawie całosc òd nowa z mëslą żebë stil béł barżi encyklopediczny, zmieniła jem ùkłôd abë lepi sã to czëtało. Całosc to tłomacznié z engwiki i plwiki. Wcyg nót je napisac dzél ò statuse òchroné i zrobic kòrekta. Brëkùje nama infobòksu na gatënczi :(
204478
wikitext
text/x-wiki
'''Lësatô kania''' (''Milvus milvus'') – gatënk drapieżnégò ptôcha z rodzëznë [[Accipitridae|Jastrzibòwatëch]]. Gatënk ten dzysdnia gniôzdëje w Eùropie, chòc w dôwnoce rozmnażôł sã téż w zôpadny Azji i północno-zôpadny Africe<ref>BirdLife International (2020). "Milvus milvus". IUCN Red List of Threatened Species. 2020 e.T22695072A181651010. doi:10.2305/IUCN.UK.2020-3.RLTS.T22695072A181651010.en. Retrieved 19 November 2021.</ref>.
== Systematika ==
[[Òbrôzk:Lunak_cerveny.jpg|mały|Dozdrzeniałô kania.]]
Lësatô kania òstała òpisónô przez Szwedzczégò rôdnika [[Karol Linneùsz|Karla Linneùsza]] w 1758 w dzesąti edycje jegò dokôzu ''Systema Naturae'' pòd binominalną gatënkòwą pòzwą ''Falco milvus''<ref>[[Karol Linneùsz|Linneùsz, Karol]] (1758) [https://www.biodiversitylibrary.org/page/726996#page/106/mode/1up Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata] (w łacëznie), Tom pierszi, Vol. 1. Stockholm: Laurentius Salvius. p. 89. "F. cera flava, cauda forsicata, corpore ferrugineo, capite albidiore."</ref>. Słowò ''Milvus'' je łaceńską pòzwą na tegò ptôcha. W 1799 francëzczi rodnik Bernard Germain de Lacépède przeniósł gatënk do rodz''aju Milvus.''
Wëapartniwóné są dwa pòdgatënczi Milvus milvus
* ''M. milvus milvus'' <small>(Linnaeus, 1758)</small> – '''lësatô kania.'''
* ''M. milvus fasciicauda'' <small>E. Hartert, 1914</small> – '''zelonogłownô kania''' – endemit Òstrowów Zelony Głowë, prawie na gwës wymarłi.
W rodzaju ''Milvus'' są téż dwa jinszé gatënczi: czôrnô kania i egipskô kania. Znóné są przëtrôfczi ùdany hybridizacji midzë lësatima i czôrnyma kaniami.
=== Hybridizacjô ===
[[Òbrôzk:Obernberg am Inn Adlerwarte Rotmilan-0136.jpg|mały|250x250px|Lësatô kania, ù skòlników z Adlerwarte Obernberg am Inn, [[Aùstriô]].]]
Rodzôj ''Milvus'' zawierô w so jesz dwa jinszé gatënczi - Czôrną kaniã i Egipską kaniã<ref>Gill, Frank; Donsker, David, eds. (2018). [http://www.worldbirdnames.org/bow/raptors/ "New World vultures, Secretarybird, kites, hawks, eagles]". World Bird List Version 8.2. International Ornithologists' Union. Retrieved 24 September 2018.</ref>. Są znóné przëtrôfczi w jaczich lësatim kanióm ùdało sã rozmnożëc z czôrnyma kaniamim, dzało sã to w niewòli jak i dzëczim stónie jak np. na Òstrowach Zelony Głowë.
Strefa hybridizacje w jaczi òba gatënczi òkazyjnie sã krziżują jistnieje w Centralny Eùropie.<ref>Heneberg, Petr; Dolinay, Matej; Matuší, Hynek; Pfeiffer, Thomas; Nachtigall, Winfried; Bizos, Jiri; Šimčiková, Daniela; Literák, Ivan (2016). [https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0159202 "Conservation of the Red Kite Milvus milvus (Aves: Accipitriformes) Is Not Affected by the Establishment of a Broad Hybrid Zone with the Black Kite Milvus migrans migrans in Central Europe"]. PLoS ONE. 11 (7). Retrieved 30 August 2025.</ref> Geneticzné badérowaniô kaniów z ti rëmi nie wëkazałe niżódnégò [[mtDNA]] czôrnëch kaniów w lësatich kaniach i vice versa. Przez to, że ten ôrt DNA je przekazëwóny przez nënczi, aùtorzë badaniô sùgerują, że mieszóné samice są bezpłodné w zgòdze z [[Prawò Haldana|prawã Haldana]].
W 2006 rokù w Szkòcji doszło do ùdanégò rozrodu samca czôrny kanie z samicą lësowati kanie w jaczim ùdało jim sã wëchòwac dwa pòdlatawce.
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus distr.png|lewo|mały|Apfelzynowi - òkres lągòwi, Mòdri - zëmòwanié, Zelony - całoroczny]]
Lësaté kanie są ptôchama jaczé zamieszkùją lëstowaté lasë, łączi, pòla dolenë i sampòwaté pògreńcza. Pòchòdzą z zôpadny [[Palearktycznô krajina|Palearktyczny krajinë]], tak tej wszëtczé znóné rodné parë (jaczich je kòl 32,200 - 37,000) młodzą sã w Eùropie.
Przede wszëtczim żëje w zôpadny, strzôdkòwi i pôłniowi Eùropie, nielëcznie téż w wschòdnym parce Azji Mniszi i w północny Africe, jaczi status nie je gwësny - lãgòwô pòpùlacja z Maroka je ùznôwónô za wëmarłą, chòc sómé kanie dali zëmùje w tim kraju. Dawni gatënk mòżna bëło pòtkac téż na Òstrowach Kanarijsczich i Òstrowach Zelonégò Przëlądka(?) gdze prawie na gwës wëmôrł<ref>Garcia-del-Rey, E. (2011). Field Guide to the Birds of Macaronesia. Azores, Madeira, Canary Islands, Cape Verde. Lynx Edicions, Barcelona, Spain.</ref>. Kania je wãdrownô, chòc to zanôlégô òd ji pòpùlacji - ptôchë z pôłni mieszkają ù se całi rok, a te żëjącé w ùmiarkòwónym klimace migrëją òd zélnikã do séwnikã nad Strzôdzemné Mòrze, abë wrócëc do swòjich gniôzd kòl strëmiannikã. W przërównanim do [[Czôrnô kania|czôrny kanie]] ji zasyg je ògraniczony.
W Pòlsce je to nielëczny lągòwi ptôch. Kanie są spòtikóné blós na zôpadze i nordowim wschòdze kraju ( Wiôlgôpòlska, [[Pòmòrskô|Pòmòrze]], nordowi part [[Dolny Sląsk|Dolnégò Sląska]] i téż [[Warmiô]] i [[Mazurë]])<ref>Ludwik Tomiałojć, Tadeusz Stawarczyk: Awifauna Polski. Rozmieszczenie, liczebność i zmiany. Wrocław: PTPP "pro Natura", 2003, s. 203. [[:pl:Specjalna:Książki/839196261X|ISBN 83-919626-1-X.]]</ref> W zaòstałich regionach je rzôdkô i je widëwónô dërch w czas migracji. Niechtërné kanie w Centralny Eùropie mògą zëmòwac kòl rakòwiszczów lub jinnëch môlów òbfitëch w pòkarm, le zachòwanié to je nieregùlarné i robią to leno pòjedinczé òsobniczi<ref>Marcin Karetta: Atlas ptaków. Pascal, 2010. I[[:pl:Specjalna:Książki/9788375136555|SBN 978-83-7513-655-5.]]</ref>.
== Znanczi gatënkù ==
[[Òbrôzk:Schwanz-Milane.jpg|mały|Ògónë kaniów pò prawi - czôrnô kania, na lewò lësatô.]]
[[Òbrôzk:Red Kite (Milvus milvus) skull at the Royal Veterinary College anatomy museum.JPG|mały|Czaszka ''Milvus Milvus''.]]
Ptóch ò wëlecałi sylwetkã i bestrim òpiórzenim. Jegò cało mô 60 - 72 cm długòscy<ref>Orta, J., Christie, D.A. & Garcia, E.F.J.: [https://www.hbw.com/species/red-kite-milvus-milvus Red Kite (Milvus milvus)]. [w:] del Hoyo, J., Elliott, A., Sargatal, J., Christie, D.A. & de Juana, E. (eds.). Handbook of the Birds of the World Alive.</ref>, i 175 - 195 cm w rozpiertoscy lecëdłów. Samce ważą òd 800 do 1200 gramów, timczasã samice są wiãkszé ważą midzë 1000, a 1300 gramów. Pòza różnicą wôdze midzë płcama ptôcha ni ma wiãkszich różniców - òbie płce są szlachòwno ùbarwióné - przeważnie dërdzawé cało z cemniejszima plecama i białima akcentama na lëcadłach. Ògón czerwònobruny z wiérzchù i jasnoszari òd spòdu, dosc mòcno wcãti (niżóden eùropejsczi drapieżnik ni mô tak mòcnégò wcãcô jak kania). Temù téż ògón je nôbarżi rozpòznowóną znanką tegò ptôcha w loce na wiôldżich wësokòscach. Głowa, szëja i placë òbsadzeniô skrzidłów jasnopòpielaté ze sztrichòwanim. Wiérzch cała dërdzawòbruny, spòdk cała jasniészi, sztrichòwóny. Na dôlny starnie skrzidła wildżi biôłi plachc, na wiérzchny starnie przeznórtny pôsk. Żółti dzëb z czórnym kùńcã, żółté nodżi. Rzadkò zdarzają sã ptôchë z [[Leùcyzm|leùcysticzną]] fòrmą. Szlachùjącô za nią czôrnô kania je përznã mniészô, mô wiele cemniészé òpiórzenié i mni wcãti ògón. W przërównanim do [[Mësznik|mësznika]] je wiele wikszô i mô dłëższé skrzidła. Pò lãgòwim czasu kanie mògą noclegòwac w karnach dochódôjącëch nawetka do sto sztëk, chòc nie je tak drësznô jak czôrnô kania.
Lësowatô kania czãsto lecy na prostëch, abò letkò zdżãtëch skrzidłach, co w sztôłce przëbôcziwô lëtrã M. W chùtczim i chwatczim loce mô prawie wiedno rozkòscérzony ògón, chtërny czasã sã przekrzéwiô.
Kanie żëją kòl 25 lat, chòc w 2023 w [[Wiôlgô Britaniô|Wiôldżi Britanii]] bëła nalezónô kania chtërnô dożëła 29 lat.<ref>[https://www.bbc.co.uk/news/uk-england-oxfordshire-65646200 "One of the first red kites released into Thames Valley dies"]. BBC News. 20 May 2023. Retrieved 31 July 2023.</ref>
== Biotop i jôda ==
[[Òbrôzk:Red Kite R1 4513.jpg|mały|Lësatô kania w loce, [[Wiôlgô Britaniô]]]]
=== Biotop ===
Widné lëstowaté i mieszóné stôré lasë rosnącé kòl łąków i pól, czãstò w òkòli dolën rzek, stôwów, czë skalëstich ùbrzégòw. Spòtikónô je na terenach niżënnech, jak téż w górach.
=== Jôda ===
Kanie są generalisticznymi drapieżnikama i scërzożércama, przez co jich dieta je tak różnorodnô, jak jich strzôdwiszcze. Pòżiwiają sã môłimi [[Krãgòwce|krãgòwcama]], przede wszëtczim drobnyma [[Sysczi|syskama]], taczimi jak: [[Toczk|toczczi]], czë [[Grëzôk|grëzôczi]], ale téz [[Wieszczórka|wieszczórczi]], [[Òwôd|òwadë]] i [[Wãdzëbôk|wãdzëbôczi]]. Zdarzëwô jim sã téż jesc rebë wëszmërgniãté na sztrąd. Ptôche te baro spôrwnie zbierają w czase nisczéò lotu dzãka czemù jachtują na swòje òfiare, rozmieją nawetka wëcygnąc bierno płëwającé na wòdze rebë.
[[Òbrôzk:Rote Milane mit Beute.jpg|lewo|mały|Pôra kaniów z òfiarą, [[Niemieckô]].]]
Wòlą chwacëc żëwą zdobëcz, niż jesc scyrzã. Zdôrzô jim sã atakòwac w célu kradzéżë òfiarë jinszé rabùszné ptôchë (nawetka te wiãkszé òd nich), czim badérowie dolmaczą znôjdowóné w ji wnãtrznoscach resztczi zwierzów, jaczich kanie nie mògłe bë ùpòlowac.
Swòji òfiarë wëzerają z gałãzy drzew, chòc òwadë i môłé sysczi rozmie chwacëc nawetka w czase wòlnégi chòdzeniô. Pòza tim lësati kanie czasã plundrëją gniôzda [[Krëkòwaté|krëkòwatëch]].
== Lągòwi cząd ==
Kanie rozmnażają sã dërch czej staną sã dwa lata stôri, chòc zdarzają sã przëtrôfczi roczniaków jaczim ùdało sã młodżec<ref>Evans, I. M.; Cordero, P. J.; Parkin, D. T. (1998). "Successful breeding at one year of age by Red Kites Milvus milvus in southern England". Ibis. 140 (1): 53–57</ref>. Są to mònogamiczné ptôchë, chtërné w pòpùlacjach stałëch pòzostają w ti sami pôrze przez zëmã, òsoblewò jeżle ptôchë pòzostają na tim sómym terytorium. Wãdrowné kanie pò zëmie wracają do tegò samégò gniôzda, czim pòdtrzimùją "wiernosc" midzë ptôchama.
=== Gniôzdo ===
[[Òbrôzk:Milmil njg 980613.jpg|lewo|mały|Młodé kanie w gniôzdze. [[Berlëno]], [[Niemieckô]].]]
Nôczãscy bùdowóné je na lëstowatim drzewie, na rozgałązeniu wietwiów, abò kònarów na wësokòscy midzë 12 a 15 métrama. Kanie wic wëbierają drzewa na skraju lasu. Bùdową gniôzda zajmùją sã òbie płce. Samc halô wietwie ò długòscy 30-50 cm, a samica je ukłôdô. Pòdstawą je grëbô galąz, scëlónô mitczimi rosclënama, ale téż różnyma smiecama np. papierã, szmatama, kòscama, czë skórama. Òba ptôche dodają wiãcy materiałów òbczas inkùbacji i òdchòwaniô młodëch. Kanie jedno gniôzdô ùżiwają przez wiele lat<ref name=":0">Cramp, Stanley; et al., eds. (1980). "Milvus milvus Red Kite". Handbook of the Birds of Europe the Middle East and North Africa: The Birds of the Western Palearctic. Vol. II: Hawks to Bustards. Oxford: Oxford University Press. pp. 36–44. <nowiki>ISBN 978-0-19-857505-4</nowiki>.</ref>. Òkazjonalnie zdarzô sã kanióm wëkòrzistac stôré gniôzda [[Mësznik|mëszników]].
Òbczas lągù kanie są agresywné wòbec rabùsznëch ptôchów, tak tej w òkòli jich gniôzda nie młodzą sã jinszé [[szpòniasté]].
=== Jaje ===
[[Òbrôzk:Milvus milvus MHNT.ZOO.2010.11.80.15.jpg|mały|Jaje lësati kani.]]
Kanie niosą sã leno rôz w rokù midzë łżëkwiatã a majã<ref name=":0" />. Pòdczas jednégò gòdowégò cządu kania niese wic midzë jednym, a trzema jajami, chòc òdnotowóné bëłë przëtrôfczi gdze w lągù bëło jaż piãc jôj. Jaje są białé z zemisto-czerwionyma kropkama. Statistycznie jajo ważi 63 gramë i mô wëmiarë 57 mm x 47 mm.<ref name=":0" /> Jeżle ląg òstónié stracóny, samica zôs zniese jaja.<ref name=":0" />
=== Wësadowanié i dorôstanié ===
Jaja wësadowóné są przez kòl 30 dni przez samicã. Samc w tim czasu przenosy ji pòkarm, chòc czasã zastãpùje swòją partnerkã czej òna lecy jachtowac. Pizglãta pierszé dwa tydzenié żëcô spãdzają w gniôzdzë razã z nënką, chtërnô karmi je jôdã halóną przez samca. Pózni òba ptôchë latają pò jestkù dlô młodëch. Pò 14 dniach młodé sóme mùszą karmic z przëniesonégò do gniôzdã jedzeniô. Kòl 45 dnia żëcô pizglãta zaczinają wspinac sã na gałãze kòło gniôzda, le rzadkò pòdlatują przed 48-50 dnia żëcô, a czasã nawetka do wiekù 60 - 70 dniów. Pòdlatwce spãdzają jesz dwa - trzë tydzenie òkòli gniôzda z dokarmiającymi jich rodzëcama. Pò dwùch latach młodé stają sã dozdrzëniałimi ptôchama.
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
[[Kategòrëjô:Ptôchë]]
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Ptôchë Eùropë]]
[[Kategòrëjô:Rabùszné Ptôchë]]
hlxywewfd27ddt58soxvxy8zt6w392l
204483
204478
2026-07-05T19:50:40Z
Rabùszniczka
19710
204483
wikitext
text/x-wiki
'''Lësatô kania''' (''Milvus milvus'') – gatënk drapieżnégò ptôcha z rodzëznë [[Accipitridae|Jastrzibòwatëch]]. Gatënk ten dzysdnia gniôzdëje w Eùropie, chòc w dôwnoce rozmnażôł sã téż w zôpadny Azji i północno-zôpadny Africe<ref>BirdLife International (2020). "Milvus milvus". IUCN Red List of Threatened Species. 2020 e.T22695072A181651010. doi:10.2305/IUCN.UK.2020-3.RLTS.T22695072A181651010.en. Retrieved 19 November 2021.</ref>.
== Systematika ==
[[Òbrôzk:Lunak_cerveny.jpg|mały|Dozdrzeniałô kania.]]
Lësatô kania òstała òpisónô przez Szwedzczégò rôdnika [[Karol Linneùsz|Karla Linneùsza]] w 1758 w dzesąti edycje jegò dokôzu ''Systema Naturae'' pòd binominalną gatënkòwą pòzwą ''Falco milvus''<ref>[[Karol Linneùsz|Linneùsz, Karol]] (1758) [https://www.biodiversitylibrary.org/page/726996#page/106/mode/1up Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata] (w łacëznie), Tom pierszi, Vol. 1. Stockholm: Laurentius Salvius. p. 89. "F. cera flava, cauda forsicata, corpore ferrugineo, capite albidiore."</ref>. Słowò ''Milvus'' je łaceńską pòzwą na tegò ptôcha. W 1799 francëzczi rodnik Bernard Germain de Lacépède przeniósł gatënk do rodz''aju Milvus.''
Wëapartniwóné są dwa pòdgatënczi Milvus milvus
* ''M. milvus milvus'' <small>(Linnaeus, 1758)</small> – '''lësatô kania.'''
* ''M. milvus fasciicauda'' <small>E. Hartert, 1914</small> – '''zelonogłownô kania''' – endemit Òstrowów Zelony Głowë, prawie na gwës wymarłi.
W rodzaju ''Milvus'' są téż dwa jinszé gatënczi: czôrnô kania i egipskô kania. Znóné są przëtrôfczi ùdany hybridizacji midzë lësatima i czôrnyma kaniami.
=== Hybridizacjô ===
[[Òbrôzk:Obernberg am Inn Adlerwarte Rotmilan-0136.jpg|mały|250x250px|Lësatô kania, ù skòlników z Adlerwarte Obernberg am Inn, [[Aùstriô]].]]
Rodzôj ''Milvus'' zawierô w so jesz dwa jinszé gatënczi - Czôrną kaniã i Egipską kaniã<ref>Gill, Frank; Donsker, David, eds. (2018). [http://www.worldbirdnames.org/bow/raptors/ "New World vultures, Secretarybird, kites, hawks, eagles]". World Bird List Version 8.2. International Ornithologists' Union. Retrieved 24 September 2018.</ref>. Są znóné przëtrôfczi w jaczich lësatim kanióm ùdało sã rozmnożëc z czôrnyma kaniamim, dzało sã to w niewòli jak i dzëczim stónie jak np. na Òstrowach Zelony Głowë.
Strefa hybridizacje w jaczi òba gatënczi òkazyjnie sã krziżują jistnieje w Centralny Eùropie.<ref>Heneberg, Petr; Dolinay, Matej; Matuší, Hynek; Pfeiffer, Thomas; Nachtigall, Winfried; Bizos, Jiri; Šimčiková, Daniela; Literák, Ivan (2016). [https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0159202 "Conservation of the Red Kite Milvus milvus (Aves: Accipitriformes) Is Not Affected by the Establishment of a Broad Hybrid Zone with the Black Kite Milvus migrans migrans in Central Europe"]. PLoS ONE. 11 (7). Retrieved 30 August 2025.</ref> Geneticzné badérowaniô kaniów z ti rëmi nie wëkazałe niżódnégò [[mtDNA]] czôrnëch kaniów w lësatich kaniach i vice versa. Przez to, że ten ôrt DNA je przekazëwóny przez nënczi, aùtorzë badaniô sùgerują, że mieszóné samice są bezpłodné w zgòdze z [[Prawò Haldana|prawã Haldana]].
W 2006 rokù w Szkòcji doszło do ùdanégò rozrodu samca czôrny kanie z samicą lësowati kanie w jaczim ùdało jim sã wëchòwac dwa pòdlatawce.
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus distr.png|lewo|mały|Apfelzynowi - òkres lągòwi, Mòdri - zëmòwanié, Zelony - całoroczny]]
Lësaté kanie są ptôchama jaczé zamieszkùją lëstowaté lasë, łączi, pòla dolenë i sampòwaté pògreńcza. Pòchòdzą z zôpadny [[Palearktycznô krajina|Palearktyczny krajinë]], tak tej wszëtczé znóné rodné parë (jaczich je kòl 32,200 - 37,000) młodzą sã w Eùropie.
Przede wszëtczim żëje w zôpadny, strzôdkòwi i pôłniowi Eùropie, nielëcznie téż w wschòdnym parce Azji Mniszi i w północny Africe, jaczi status nie je gwësny - lãgòwô pòpùlacja z Maroka je ùznôwónô za wëmarłą, chòc sómé kanie dali zëmùje w tim kraju. Dawni gatënk mòżna bëło pòtkac téż na Òstrowach Kanarijsczich i Òstrowach Zelonégò Przëlądka(?) gdze prawie na gwës wëmôrł<ref>Garcia-del-Rey, E. (2011). Field Guide to the Birds of Macaronesia. Azores, Madeira, Canary Islands, Cape Verde. Lynx Edicions, Barcelona, Spain.</ref>. Kania je wãdrownô, chòc to zanôlégô òd ji pòpùlacji - ptôchë z pôłni mieszkają ù se całi rok, a te żëjącé w ùmiarkòwónym klimace migrëją òd zélnikã do séwnikã nad Strzôdzemné Mòrze, abë wrócëc do swòjich gniôzd kòl strëmiannikã. W przërównanim do [[Czôrnô kania|czôrny kanie]] ji zasyg je ògraniczony.
W Pòlsce je to nielëczny lągòwi ptôch. Kanie są spòtikóné blós na zôpadze i nordowim wschòdze kraju ( Wiôlgôpòlska, [[Pòmòrskô|Pòmòrze]], nordowi part [[Dolny Sląsk|Dolnégò Sląska]] i téż [[Warmiô]] i [[Mazurë]])<ref>Ludwik Tomiałojć, Tadeusz Stawarczyk: Awifauna Polski. Rozmieszczenie, liczebność i zmiany. Wrocław: PTPP "pro Natura", 2003, s. 203. [[:pl:Specjalna:Książki/839196261X|ISBN 83-919626-1-X.]]</ref> W zaòstałich regionach je rzôdkô i je widëwónô dërch w czas migracji. Niechtërné kanie w Centralny Eùropie mògą zëmòwac kòl rakòwiszczów lub jinnëch môlów òbfitëch w pòkarm, le zachòwanié to je nieregùlarné i robią to leno pòjedinczé òsobniczi<ref>Marcin Karetta: Atlas ptaków. Pascal, 2010. I[[:pl:Specjalna:Książki/9788375136555|SBN 978-83-7513-655-5.]]</ref>.
== Znanczi gatënkù ==
[[Òbrôzk:Schwanz-Milane.jpg|mały|Ògónë kaniów pò prawi - czôrnô kania, na lewò lësatô.]]
[[Òbrôzk:Red Kite (Milvus milvus) skull at the Royal Veterinary College anatomy museum.JPG|mały|Czaszka ''Milvus Milvus''.]]
Ptóch ò wëlecałi sylwetkã i bestrim òpiórzenim. Jegò cało mô 60 - 72 cm długòscy<ref>Orta, J., Christie, D.A. & Garcia, E.F.J.: [https://www.hbw.com/species/red-kite-milvus-milvus Red Kite (Milvus milvus)]. [w:] del Hoyo, J., Elliott, A., Sargatal, J., Christie, D.A. & de Juana, E. (eds.). Handbook of the Birds of the World Alive.</ref>, i 175 - 195 cm w rozpiertoscy lecëdłów. Samce ważą òd 800 do 1200 gramów, timczasã samice są wiãkszé ważą midzë 1000, a 1300 gramów. Pòza różnicą wôdze midzë płcama ptôcha ni ma wiãkszich różniców - òbie płce są szlachòwno ùbarwióné - przeważnie dërdzawé cało z cemniejszima plecama i białima akcentama na lëcadłach. Ògón czerwònobruny z wiérzchù i jasnoszari òd spòdu, dosc mòcno wcãti (niżóden eùropejsczi drapieżnik ni mô tak mòcnégò wcãcô jak kania). Temù téż ògón je nôbarżi rozpòznowóną znanką tegò ptôcha w loce na wiôldżich wësokòscach. Głowa, szëja i placë òbsadzeniô skrzidłów jasnopòpielaté ze sztrichòwanim. Wiérzch cała dërdzawòbruny, spòdk cała jasniészi, sztrichòwóny. Na dôlny starnie skrzidła wildżi biôłi plachc, na wiérzchny starnie przeznórtny pôsk. Żółti dzëb z czórnym kùńcã, żółté nodżi. Rzadkò zdarzają sã ptôchë z [[Leùcyzm|leùcysticzną]] fòrmą. Szlachùjącô za nią czôrnô kania je përznã mniészô, mô wiele cemniészé òpiórzenié i mni wcãti ògón. W przërównanim do [[Mësznik|mësznika]] je wiele wikszô i mô dłëższé skrzidła. Pò lãgòwim czasu kanie mògą noclegòwac w karnach dochódôjącëch nawetka do sto sztëk, chòc nie je tak drësznô jak czôrnô kania.
Lësowatô kania czãsto lecy na prostëch, abò letkò zdżãtëch skrzidłach, co w sztôłce przëbôcziwô lëtrã M. W chùtczim i chwatczim loce mô prawie wiedno rozkòscérzony ògón, chtërny czasã sã przekrzéwiô.
Kanie żëją kòl 25 lat, chòc w 2023 w [[Wiôlgô Britaniô|Wiôldżi Britanii]] bëła nalezónô kania chtërnô dożëła 29 lat.<ref>[https://www.bbc.co.uk/news/uk-england-oxfordshire-65646200 "One of the first red kites released into Thames Valley dies"]. BBC News. 20 May 2023. Retrieved 31 July 2023.</ref>
== Biotop i jôda ==
[[Òbrôzk:Red Kite R1 4513.jpg|mały|Lësatô kania w loce, [[Wiôlgô Britaniô]]]]
=== Biotop ===
Widné lëstowaté i mieszóné stôré lasë rosnącé kòl łąków i pól, czãstò w òkòli dolën rzek, stôwów, czë skalëstich ùbrzégòw. Spòtikónô je na terenach niżënnech, jak téż w górach.
=== Jôda ===
Kanie są generalisticznymi drapieżnikama i scërzożércama, przez co jich dieta je tak różnorodnô, jak jich strzôdwiszcze. Pòżiwiają sã môłimi [[Krãgòwce|krãgòwcama]], przede wszëtczim drobnyma [[Sysczi|syskama]], taczimi jak: [[Toczk|toczczi]], czë [[Grëzôk|grëzôczi]], ale téz [[Wieszczórka|wieszczórczi]], [[Òwôd|òwadë]] i [[Wãdzëbôk|wãdzëbôczi]]. Zdarzëwô jim sã téż jesc rebë wëszmërgniãté na sztrąd. Ptôche te baro spôrwnie zbierają w czase nisczéò lotu dzãka czemù jachtują na swòje òfiare, rozmieją nawetka wëcygnąc bierno płëwającé na wòdze rebë.
[[Òbrôzk:Rote Milane mit Beute.jpg|lewo|mały|Pôra kaniów z òfiarą, [[Niemieckô]].]]
Wòlą chwacëc żëwą zdobëcz, niż jesc scyrzã. Zdôrzô jim sã atakòwac w célu kradzéżë òfiarë jinszé rabùszné ptôchë (nawetka te wiãkszé òd nich), czim badérowie dolmaczą znôjdowóné w ji wnãtrznoscach resztczi zwierzów, jaczich kanie nie mògłe bë ùpòlowac.
Swòji òfiarë wëzerają z gałãzy drzew, chòc òwadë i môłé sysczi rozmie chwacëc nawetka w czase wòlnégi chòdzeniô. Pòza tim lësati kanie czasã plundrëją gniôzda [[Krëkòwaté|krëkòwatëch]].
== Lągòwi cząd ==
[[Òbrôzk:Horst des Rotmilans (Milvus milvus).JPG|mały|Gnôzdo kani.]]
Kanie rozmnażają sã dërch czej staną sã dwa lata stôri, chòc zdarzają sã przëtrôfczi roczniaków jaczim ùdało sã młodżec<ref>Evans, I. M.; Cordero, P. J.; Parkin, D. T. (1998). "Successful breeding at one year of age by Red Kites Milvus milvus in southern England". Ibis. 140 (1): 53–57</ref>. Są to mònogamiczné ptôchë, chtërné w pòpùlacjach stałëch pòzostają w ti sami pôrze przez zëmã, òsoblewò jeżle ptôchë pòzostają na tim sómym terytorium. Wãdrowné kanie pò zëmie wracają do tegò samégò gniôzda, czim pòdtrzimùją "wiernosc" midzë ptôchama.
=== Gniôzdo ===
[[Òbrôzk:Milmil njg 980613.jpg|lewo|mały|Młodé kanie w gniôzdze. [[Berlëno]], [[Niemieckô]].]]
Nôczãscy bùdowóné je na lëstowatim drzewie, na rozgałązeniu wietwiów, abò kònarów na wësokòscy midzë 12 a 15 métrama. Kanie wic wëbierają drzewa na skraju lasu. Bùdową gniôzda zajmùją sã òbie płce. Samc halô wietwie ò długòscy 30-50 cm, a samica je ukłôdô. Pòdstawą je grëbô galąz, scëlónô mitczimi rosclënama, ale téż różnyma smiecama np. papierã, szmatama, kòscama, czë skórama. Òba ptôche dodają wiãcy materiałów òbczas inkùbacji i òdchòwaniô młodëch. Kanie jedno gniôzdô ùżiwają przez wiele lat<ref name=":0">Cramp, Stanley; et al., eds. (1980). "Milvus milvus Red Kite". Handbook of the Birds of Europe the Middle East and North Africa: The Birds of the Western Palearctic. Vol. II: Hawks to Bustards. Oxford: Oxford University Press. pp. 36–44. <nowiki>ISBN 978-0-19-857505-4</nowiki>.</ref>. Òkazjonalnie zdarzô sã kanióm wëkòrzistac stôré gniôzda [[Mësznik|mëszników]].
Òbczas lągù kanie są agresywné wòbec rabùsznëch ptôchów, tak tej w òkòli jich gniôzda nie młodzą sã jinszé [[szpòniasté]].
=== Jaje ===
[[Òbrôzk:Milvus milvus MHNT.ZOO.2010.11.80.15.jpg|mały|Jaje lësati kani.]]
Kanie niosą sã leno rôz w rokù midzë łżëkwiatã a majã<ref name=":0" />. Pòdczas jednégò gòdowégò cządu kania niese wic midzë jednym, a trzema jajami, chòc òdnotowóné bëłë przëtrôfczi gdze w lągù bëło jaż piãc jôj. Jaje są białé z zemisto-czerwionyma kropkama. Statistycznie jajo ważi 63 gramë i mô wëmiarë 57 mm x 47 mm.<ref name=":0" /> Jeżle ląg òstónié stracóny, samica zôs zniese jaja.<ref name=":0" />
=== Wësadowanié i dorôstanié ===
Jaja wësadowóné są przez kòl 30 dni przez samicã. Samc w tim czasu przenosy ji pòkarm, chòc czasã zastãpùje swòją partnerkã czej òna lecy jachtowac. Pizglãta pierszé dwa tydzenié żëcô spãdzają w gniôzdzë razã z nënką, chtërnô karmi je jôdã halóną przez samca. Pózni òba ptôchë latają pò jestkù dlô młodëch. Pò 14 dniach młodé sóme mùszą karmic z przëniesonégò do gniôzdã jedzeniô. Kòl 45 dnia żëcô pizglãta zaczinają wspinac sã na gałãze kòło gniôzda, le rzadkò pòdlatują przed 48-50 dnia żëcô, a czasã nawetka do wiekù 60 - 70 dniów. Pòdlatwce spãdzają jesz dwa - trzë tydzenie òkòli gniôzda z dokarmiającymi jich rodzëcama. Pò dwùch latach młodé stają sã dozdrzëniałimi ptôchama.
== Galeriô ==
[[Òbrôzk:Leucistic Red Kite - Flickr - Andy Morffew.jpg|lewo|mały|Leùcysticzny òsobnik.]]
[[Òbrôzk:Milhafre-real, Red Kite (52539996534).jpg|centruj|mały|Lësatô kania w Pòrtugalii.]]
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
[[Kategòrëjô:Ptôchë]]
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Ptôchë Eùropë]]
[[Kategòrëjô:Rabùszné Ptôchë]]
45il73vxtxqbusulx7rg4txt17jgd6o
204486
204483
2026-07-05T20:24:11Z
Terrus38
14026
kòrekta
204486
wikitext
text/x-wiki
'''Lësatô kania''' (''Milvus milvus'') – gatënk drapieżnégò ptôcha z rodzëznë [[Accipitridae|jastrzëbòwatëch]]. Gatënk ten dzysdnia gniôzdëje w Eùropie, chòc w dôwnoce rozmnôżôł sã téż w zôpadny Azji i północno-zôpadny Africe<ref>BirdLife International (2020). "Milvus milvus". IUCN Red List of Threatened Species. 2020 e.T22695072A181651010. doi:10.2305/IUCN.UK.2020-3.RLTS.T22695072A181651010.en. Retrieved 19 November 2021.</ref>.
== Systematika ==
[[Òbrôzk:Lunak_cerveny.jpg|mały|Dozdrzeniałô kania]]
Lësatô kania òstała òpisónô przez szwedzczégò przërodnika [[Karol Linneùsz|Karola Linneùsza]] w 1758 r., w dzesąti edycje jegò dokazu ''Systema Naturae'' pòd binominalną gatënkòwą pòzwą ''Falco milvus''<ref>[[Karol Linneùsz|Linneùsz, Karol]] (1758) [https://www.biodiversitylibrary.org/page/726996#page/106/mode/1up Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata] (w łacëznie), Tom pierszi, Vol. 1. Stockholm: Laurentius Salvius. p. 89. "F. cera flava, cauda forsicata, corpore ferrugineo, capite albidiore."</ref>. Słowò ''Milvus'' je łacyńską pòzwą na tegò ptôcha. W 1799 francësczi przëodnik Bernard Germain de Lacépède przeniósł gatënk do rodz''aju Milvus.''
Wëapartniwóné są dwa pòdgatënczi Milvus milvus
* ''M. milvus milvus'' <small>(Linnaeus, 1758)</small> – '''lësatô kania.'''
* ''M. milvus fasciicauda'' <small>E. Hartert, 1914</small> – '''zelonogłowskô kania''' – endemit [[Òstrowë Zelony Głowë|Òstrowów Zelony Głowë]], prawie na gwës wëmarłi.
W rodzaju ''Milvus'' są téż dwa jinszé gatënczi: czôrnô kania i egipskô kania. Znóné są przëtrôfczi ùdóny hibridizacji midzë lësatima i czôrnyma kaniama.
=== Hibridizacjô ===
[[Òbrôzk:Obernberg am Inn Adlerwarte Rotmilan-0136.jpg|mały|250x250px|Lësatô kania, [[Aùstriô]]]]
Rodzôj ''Milvus'' zawiérô w se jesz dwa jinszé gatënczi - czôrną kaniã i egipską kaniã<ref>Gill, Frank; Donsker, David, eds. (2018). [http://www.worldbirdnames.org/bow/raptors/ "New World vultures, Secretarybird, kites, hawks, eagles]". World Bird List Version 8.2. International Ornithologists' Union. Retrieved 24 September 2018.</ref>. Są znóné przëtrôfczi, w jaczich lësatim kanióm ùdało sã rozmnożëc z czôrnyma kaniama, dzało sã to tak w niewòli, jak i dzëczim stanie, np. na Òstrowach Zelony Głowë.
Strefa hibridizacje w jaczi òba gatënczi òkazyjnie sã krziżëją, jistnieje w Centralny Eùropie.<ref>Heneberg, Petr; Dolinay, Matej; Matuší, Hynek; Pfeiffer, Thomas; Nachtigall, Winfried; Bizos, Jiri; Šimčiková, Daniela; Literák, Ivan (2016). [https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0159202 "Conservation of the Red Kite Milvus milvus (Aves: Accipitriformes) Is Not Affected by the Establishment of a Broad Hybrid Zone with the Black Kite Milvus migrans migrans in Central Europe"]. PLoS ONE. 11 (7). Retrieved 30 August 2025.</ref> Geneticzné badérowaniô kaniów z te òbrëmiô nie wëkôzałë niżódnégò [[mtDNA]] czôrnëch kaniów w lësatëch kaniach i vice versa. Przez to, że ten ôrt DNA je przekazéwóny przez nënczi, aùtorzë badaniô sùgerëją, że miészóné samice są bezpłodné zgòdą z [[Prawò Haldana|prawã Haldana]].
W 2006 rokù w Szkòcji doszło do ùdónégò rozrodu samca czôrny kanie z samicą lësati kanie w jaczim ùdało jim sã wëchòwac dwa pòdlatawce.
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus distr.png|lewo|mały|Apfelzynowi – lãgòwi cząd; mòdri – zëmòwanié; zelony – całoroczné wëstãpòwanié]]
Lësaté kanie są ptôchama, jaczé zamieszkùją lëstowaté lasë, łączi, pòla, dolëznë i sapòwaté pògrańcza. Pòchòdzą z zôpadny [[Palearkticznô krôjna|Palearkticzny krôjnë]], tak tej wszëtczé znóné rodné parë (jaczich je kòl 32,200–37,000) młodzą sã w Eùropie.
Przede wszëtczim żëje w zôpadny, westrzédny i pôłniowi Eùropie, nielëcznie téż we wschódnym parce Azji Mniészi i w północny Africe, gdze sztatus nie je gwësny - lãgòwô pòpùlacjô z Maroka je ùznôwónô za wëmarłą, chòc same kanie dali zëmùją w tim kraju. Dôwni gatënk szło pòtkac téż na Kanarijsczich Òstrowach i Òstrowach Zelony Głowë, gdze prawie na gwës wëmarł<ref>Garcia-del-Rey, E. (2011). Field Guide to the Birds of Macaronesia. Azores, Madeira, Canary Islands, Cape Verde. Lynx Edicions, Barcelona, Spain.</ref>. Kania je wãdrownô, chòc to zanôlégô òd ji pòpùlacji - ptôchë z pôłniô mieszkają ù se całi rok, a te żëjącé w ùmiarkòwónym klimace migrëją òd zélnikã do séwnikã nad [[Strzódzemné Mòrze]], abë wrócëc do swòjich gniôzd kòl strëmiannika. W przërównanim do [[Czôrnô kania|czôrny kanie]], ji syg je ògraniczony.
W Pòlsce je to nielëczny lãgòwi ptôch. Kanie są pòtikóné blós na zôpadze i nordowim wschòdze kraju ([[Wiôlgôpòlskô]], [[Pòmòrskô|Pòmòrzé]], nordowi part [[Dólny Szląsk|Dólnégò Szląska]] a téż [[Warmiô]] i [[Mazurë]])<ref>Ludwik Tomiałojć, Tadeusz Stawarczyk: Awifauna Polski. Rozmieszczenie, liczebność i zmiany. Wrocław: PTPP "pro Natura", 2003, s. 203. [[:pl:Specjalna:Książki/839196261X|ISBN 83-919626-1-X.]]</ref> W zaòstałëch regionach je rzôdkô, widiwónô je òb czas migracji. Niechtërne kanie w Centralny Eùropie mògą zëmòwac kòl rakòwiszczów lub jinëch môlów bòkadnëch w jôdã, le zachòwanié to je nieregùlarné i robią to leno pòjedinczé òsobniczi<ref>Marcin Karetta: Atlas ptaków. Pascal, 2010. I[[:pl:Specjalna:Książki/9788375136555|SBN 978-83-7513-655-5.]]</ref>.
== Znanczi gatënkù ==
[[Òbrôzk:Schwanz-Milane.jpg|mały|Ògònë kaniów: pò prawi czôrnô kania, na lewą – lësatô]]
[[Òbrôzk:Red Kite (Milvus milvus) skull at the Royal Veterinary College anatomy museum.JPG|mały|Czaszka ''Milvus Milvus'']]
Ptôch charakterizëje sã wëlecałą sztaturą i bestrim òpiórzenim. Jegò cało mô 60–72 cm długòscë<ref>Orta, J., Christie, D.A. & Garcia, E.F.J.: [https://www.hbw.com/species/red-kite-milvus-milvus Red Kite (Milvus milvus)]. [w:] del Hoyo, J., Elliott, A., Sargatal, J., Christie, D.A. & de Juana, E. (eds.). Handbook of the Birds of the World Alive.</ref>, a rozcygłosc jegò lecëdłów wënôszô 175–195 cm. Samce wôżą òd 800 do 1200 gramów, timczasã samice są wiãkszé i ważą midzë 1000 a 1300 gramów. Pòza różnicą we wôdze, midzë płcama ptôcha ni ma wiãkszich różniców - òbie pëłce są szlachòwno ùbarwióné – dërdzawé cało z cemniészima plecama i biôłima akcentama na lëcadłach. Ògón czerwònobruny z wiérzchù i jasnoszari òd spòdu, dosc mòcno wcãti (niżóden eùropejsczi drapieżnik ni mô tak mòcnégò wcãcô jak kania). Temù téż ògón je nôbarżi rozpòznôwalną znanką tegò ptôcha w loce na wiôldżich wësokòscach. Głowa, szëja i place òbsadzeniô skrzidłów jasnopòpielaté ze sztrichòwanim. Wiérzch cała dërdzawòbruny, spódk cała jasniészi, sztrichòwóny. Na dólny starnie skrzidła to dô wiôldżi biôłi plachc, na wiérzchny starnie przeznórtny pasyk. Żôłti dzób z czôrnym kùńcã, żółté nodżi. Rzôdkò zdôrzają sã ptôchë z [[Leùcyzm|leùcysticzną]] fòrmą. Szlachùjącô za nią czôrnô kania je përznã mniészô, mô wiele cemniészé òpiórzenié i mni wcãti ògón. W przërównanim do [[Mësznik|mësznika]] je wiele wikszô i mô dłëższé skrzidła. Pò lãgòwim czasu kanie mògą noclegòwac w karnach dochôdającëch nawetka do sta sztëk, chòc nie je tak drësznô jak czôrnô kania.
Lësatô kania czãsto lecy na prostëch abò letkò zdżãtëch skrzidłach, co w sztôłce przëbacziwô lëtrã M. W chùtczim i chwatczim loce mô prawie wiedno rozkòscérzony ògón, chtëren czasã sã przekrzéwiô.
Kanie żëją kòl 25 lat, chòc w 2023 we [[Wiôlgô Britaniô|Wiôldżi Britanii]] òstała nalazłô kania, chtërna dożëła 29 lat.<ref>[https://www.bbc.co.uk/news/uk-england-oxfordshire-65646200 "One of the first red kites released into Thames Valley dies"]. BBC News. 20 May 2023. Retrieved 31 July 2023.</ref>
== Biotop i jôda ==
[[Òbrôzk:Red Kite R1 4513.jpg|mały|Lësatô kania w loce, [[Wiôlgô Britaniô]]]]
=== Biotop ===
Widné, lëstowaté i mieszóné, stôré lasë roscącé kòl łąków i pól, czãstò w òkòlim dolëznów rzék, stawów, czë skalëstëch ùbrzegòw. Pòtikónô je na nizynnëch terenach, jak téż w górach.
=== Jôda ===
Kanie są generalisticznyma drapieżnikama i scërzożércama, przez co jich dieta je tak różnorodnô, jak jich strzodowiszcze. Pòżiwiają sã môłima [[Krãgówce|krãgówcama]], przede wszëtczim drobnyma [[Sysczi|syskama]], taczima jak: [[Toczk|toczczi]], czë [[Grëzôk|grëzôczi]], ale téż [[Wieszczórka|wieszczórczi]], [[Òwôd|òwadë]] i [[Wãdzëbôk|wãdzëbôczi]]. Zdarziwô jima sã téż jesc rëbë wëszmërgniãté na sztrąd. Ptôchë te baro sprôwnie zbiérają òb czas nisczégò lotu, dzãka czemù jachtëją na swòje òfiarë. Rozmieją nawetka wëcygnąc biérno płëwającé na wòdze rebë.
[[Òbrôzk:Rote Milane mit Beute.jpg|lewo|mały|Pôra kaniów z òfiarą, [[Niemieckô]]]]
Òne wòlą chwacëc żëwą zdobëcz, niż jesc scërza. Zdôrzô jim sã atakòwac jinszé rabùszné ptôchë (nawetka te wiãkszé òd nich) w célu kradzeżë jichny òfiarë, czim badérowie dolmaczą nalôżóné w ji wnãtrznoscach resztczi zwiérzãt, jaczich kanie ni mògłebë ùpòlowac.
Swòjich òfiarów wëzérają z gałãzów drzéwiãt, chòc òwadë i môłé sysczi rozmieje schwacëc nawetka òb czas wòlnégò chòdzeniô. Pòza tim lësaté kanie czasã plundrëją gniôzda [[Krëkòwaté|krëkòwatëch]].
== Lãgòwi cząd ==
[[Òbrôzk:Horst des Rotmilans (Milvus milvus).JPG|mały|Gniôzdo kani]]
Kanie rozmnôżają sã, czej staną sã dwa lata stôré, chòc zdôrzają sã przëtrôfczi roczniôków, jaczim ùdało sã młodzëc<ref>Evans, I. M.; Cordero, P. J.; Parkin, D. T. (1998). "Successful breeding at one year of age by Red Kites Milvus milvus in southern England". Ibis. 140 (1): 53–57</ref>. Są to mònogamiczné ptôchë, chtërne w stałëch pòpùlacjach òstają w ti sami pôrze przez zëmã, òsoblëwò jeżlë ptôchë òstają na tim samim teritorium. Wãdrowné kanie pò zëmie wrôcają do tegò samégò gniôzda, czim pòdtrzimùją wiérnosc midzë ptôchama.
=== Gniôzdo ===
[[Òbrôzk:Milmil njg 980613.jpg|lewo|mały|Młodé kanie w gniôzdze. [[Berlëno]], [[Niemieckô]].]]
Nôczãscy bùdowóné je na lëstowatim drzewie, na rozwietwienim gałãzów abò twélów, na wësokòscë midzë 12 a 15 métrów. Kanie wic wëbiérają drzewa na kraju lasa. Bùdową gniôzda zajmùją sã òbie pëłce. Samc haléwô wietwie ò długòscë 30–50 cm, a samica je ukłôdô. Pòdstawą je grëbô gałąz, scelonô mitczima roscënama, ale téż różnyma smiecama, np. papiorã, kòdrama, gnôtama czë skórama. Òba ptôchë dodają wiãcy materiałów òb czas inkùbacji i chòwaniô młodëch. Kanie jedno gniôzdô ùżiwają przez wiele lat<ref name=":0">Cramp, Stanley; et al., eds. (1980). "Milvus milvus Red Kite". Handbook of the Birds of Europe the Middle East and North Africa: The Birds of the Western Palearctic. Vol. II: Hawks to Bustards. Oxford: Oxford University Press. pp. 36–44. <nowiki>ISBN 978-0-19-857505-4</nowiki>.</ref>. Òkazjonalnie zdôrzô sã kanióm wëkòrzëstac stôré gniôzda [[Mësznik|mëszników]].
Òb czas lãgù kanie są agresywné wedle rabùsznëch ptôchów, tak tej w òkòlim jich gniôzda nie młodzą sã jinszé [[grabcaté]].
=== Jaja ===
[[Òbrôzk:Milvus milvus MHNT.ZOO.2010.11.80.15.jpg|mały|Jaje lësati kani]]
Kanie niesą sã leno rôz w rokù, midzë łżëkwiatã a majã<ref name=":0" />. Òb czas jednégò gòdowégò cządu kania niese wic midzë jednym, a trzema jajama, chòc òdnotowóné bëłë przëtrôfczi, gdze w lãgù bëło jaż piãc jôj. Jaja są biôłé z zemisto-czerwionyma kropkama. Statisticznie jaje wôżi 63 gramë i mô wëmiarë 57 mm x 47 mm.<ref name=":0" /> Jeżlë ląg òstónie stracóny, samica zôs zniese jaja.<ref name=":0" />
=== Wësadowanié i dorôstanié ===
Jaja wësadowóné są przez kòl 30 dni przez samicã. Samc w tim czasu przënosy ji jôdã, chòc czasã zastãpùje swòją partnerkã, czej òna lecy jachtowac. Pizglãta pierszé dwa tidzenié żëcô spãdzywają w gniôzdze razã z nënką, chtërna kôrmi je jôdą halóną przez samca. Pózni òba ptôchë lôtają pò jestkù dlô młodëch. Pò 14 dniach młodé same mùszą sã karmic przëniosłim do gniôzda jedzenim. Kòl 45 dnia żëcô pizglãta zaczinają sã rãplowac na gałãze kòl gniôzda, le rzôdkò pòdlatują przed 48–50 dniã żëcô, a czasã nawetka do wiekù 60–70 dniów. Pòdlatawce spãdzywają jesz dwa do trzech tidzeniów w pòblëżim gniôzda z dokarmiającyma jich starszima. Pò dwùch latach młodé stają sã dozdrzeniałima ptôchama.
== Galeriô ==
[[Òbrôzk:Leucistic Red Kite - Flickr - Andy Morffew.jpg|lewo|mały|Leùcysticzny òsobnik]]
[[Òbrôzk:Milhafre-real, Red Kite (52539996534).jpg|centruj|mały|Lësatô kania w Pòrtugalii]]
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
[[Kategòrëjô:Ptôchë]]
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Ptôchë Eùropë]]
[[Kategòrëjô:Rabùszné Ptôchë]]
nsmnvhs9p8aa3ln934ycqxnzxulfzqk
204492
204486
2026-07-06T07:43:55Z
Rabùszniczka
19710
204492
wikitext
text/x-wiki
'''Lësatô kania''' (''Milvus milvus'') – gatënk drapieżnégò ptôcha z rodzëznë [[Accipitridae|jastrzëbòwatëch]]. Gatënk ten dzysdnia gniôzdëje w Eùropie, chòc w dôwnoce rozmnôżôł sã téż w zôpadny Azji i północno-zôpadny Africe<ref>BirdLife International (2020). "Milvus milvus". IUCN Red List of Threatened Species. 2020 e.T22695072A181651010. doi:10.2305/IUCN.UK.2020-3.RLTS.T22695072A181651010.en. Retrieved 19 November 2021.</ref>.
== Systematika ==
[[Òbrôzk:Lunak_cerveny.jpg|mały|Dozdrzeniałô kania]]
Lësatô kania òstała òpisónô przez szwedzczégò przërodnika [[Karol Linneùsz|Karola Linneùsza]] w 1758 r., w dzesąti edycje jegò dokazu ''Systema Naturae'' pòd binominalną gatënkòwą pòzwą ''Falco milvus''<ref>[[Karol Linneùsz|Linneùsz, Karol]] (1758) [https://www.biodiversitylibrary.org/page/726996#page/106/mode/1up Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata] (w łacëznie), Tom pierszi, Vol. 1. Stockholm: Laurentius Salvius. p. 89. "F. cera flava, cauda forsicata, corpore ferrugineo, capite albidiore."</ref>. Słowò ''Milvus'' je łacyńską pòzwą na tegò ptôcha. W 1799 francësczi przëodnik Bernard Germain de Lacépède przeniósł gatënk do rodz''aju Milvus.''
Wëapartniwóné są dwa pòdgatënczi Milvus milvus
* ''M. milvus milvus'' <small>(Linnaeus, 1758)</small> – '''lësatô kania.'''
* ''M. milvus fasciicauda'' <small>E. Hartert, 1914</small> – '''zelonogłowskô kania''' – endemit [[Òstrowë Zelony Głowë|Òstrowów Zelony Głowë]], prawie na gwës wëmarłi.
W rodzaju ''Milvus'' są téż dwa jinszé gatënczi: czôrnô kania i egipskô kania. Znóné są przëtrôfczi ùdóny hibridizacji midzë lësatima i czôrnyma kaniama.
=== Hibridizacjô ===
[[Òbrôzk:Obernberg am Inn Adlerwarte Rotmilan-0136.jpg|mały|250x250px|Lësatô kania, [[Aùstriô]]]]
Rodzôj ''Milvus'' zawiérô w se jesz dwa jinszé gatënczi - czôrną kaniã i egipską kaniã<ref>Gill, Frank; Donsker, David, eds. (2018). [http://www.worldbirdnames.org/bow/raptors/ "New World vultures, Secretarybird, kites, hawks, eagles]". World Bird List Version 8.2. International Ornithologists' Union. Retrieved 24 September 2018.</ref>. Są znóné przëtrôfczi, w jaczich lësatim kanióm ùdało sã rozmnożëc z czôrnyma kaniama, dzało sã to tak w niewòli, jak i dzëczim stanie, np. na Òstrowach Zelony Głowë.
Strefa hibridizacje w jaczi òba gatënczi òkazyjnie sã krziżëją, jistnieje w Centralny Eùropie.<ref>Heneberg, Petr; Dolinay, Matej; Matuší, Hynek; Pfeiffer, Thomas; Nachtigall, Winfried; Bizos, Jiri; Šimčiková, Daniela; Literák, Ivan (2016). [https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0159202 "Conservation of the Red Kite Milvus milvus (Aves: Accipitriformes) Is Not Affected by the Establishment of a Broad Hybrid Zone with the Black Kite Milvus migrans migrans in Central Europe"]. PLoS ONE. 11 (7). Retrieved 30 August 2025.</ref> Geneticzné badérowaniô kaniów z te òbrëmiô nie wëkôzałë niżódnégò [[mtDNA]] czôrnëch kaniów w lësatëch kaniach i vice versa. Przez to, że ten ôrt DNA je przekazéwóny przez nënczi, aùtorzë badaniô sùgerëją, że miészóné samice są bezpłodné zgòdą z [[Prawò Haldana|prawã Haldana]].
W 2006 rokù w Szkòcji doszło do ùdónégò rozrodu samca czôrny kanie z samicą lësati kanie w jaczim ùdało jim sã wëchòwac dwa pòdlatawce.
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus distr.png|lewo|mały|Apfelzynowi – lãgòwi cząd; mòdri – zëmòwanié; zelony – całoroczné wëstãpòwanié]]
Lësaté kanie są ptôchama, jaczé zamieszkùją lëstowaté lasë, łączi, pòla, dolëznë i sapòwaté pògrańcza. Pòchòdzą z zôpadny [[Palearkticznô krôjna|Palearkticzny krôjnë]], tak tej wszëtczé znóné rodné parë (jaczich je kòl 32,200–37,000) młodzą sã w Eùropie.
Przede wszëtczim żëje w zôpadny, westrzédny i pôłniowi Eùropie, nielëcznie téż we wschódnym parce Azji Mniészi i w północny Africe, gdze sztatus nie je gwësny - lãgòwô pòpùlacjô z Maroka je ùznôwónô za wëmarłą, chòc same kanie dali zëmùją w tim kraju. Dôwni gatënk szło pòtkac téż na Kanarijsczich Òstrowach i Òstrowach Zelony Głowë, gdze prawie na gwës wëmarł<ref>Garcia-del-Rey, E. (2011). Field Guide to the Birds of Macaronesia. Azores, Madeira, Canary Islands, Cape Verde. Lynx Edicions, Barcelona, Spain.</ref>. Kania je wãdrownô, chòc to zanôlégô òd ji pòpùlacji - ptôchë z pôłniô mieszkają ù se całi rok, a te żëjącé w ùmiarkòwónym klimace migrëją òd zélnikã do séwnikã nad [[Strzódzemné Mòrze]], abë wrócëc do swòjich gniôzd kòl strëmiannika. W przërównanim do [[Czôrnô kania|czôrny kanie]], ji syg je ògraniczony.
W Pòlsce je to nielëczny lãgòwi ptôch. Kanie są pòtikóné blós na zôpadze i nordowim wschòdze kraju ([[Wiôlgôpòlskô]], [[Pòmòrskô|Pòmòrzé]], nordowi part [[Dólny Szląsk|Dólnégò Szląska]] a téż [[Warmiô]] i [[Mazurë]])<ref>Ludwik Tomiałojć, Tadeusz Stawarczyk: Awifauna Polski. Rozmieszczenie, liczebność i zmiany. Wrocław: PTPP "pro Natura", 2003, s. 203. [[:pl:Specjalna:Książki/839196261X|ISBN 83-919626-1-X.]]</ref> W zaòstałëch regionach je rzôdkô, widiwónô je òb czas migracji. Niechtërne kanie w Centralny Eùropie mògą zëmòwac kòl rakòwiszczów lub jinëch môlów bòkadnëch w jôdã, le zachòwanié to je nieregùlarné i robią to leno pòjedinczé òsobniczi<ref>Marcin Karetta: Atlas ptaków. Pascal, 2010. I[[:pl:Specjalna:Książki/9788375136555|SBN 978-83-7513-655-5.]]</ref>.
== Znanczi gatënkù ==
[[Òbrôzk:Schwanz-Milane.jpg|mały|Ògònë kaniów: pò prawi czôrnô kania, na lewą – lësatô]]
[[Òbrôzk:Red Kite (Milvus milvus) skull at the Royal Veterinary College anatomy museum.JPG|mały|Czaszka ''Milvus Milvus'']]
Ptôch charakterizëje sã wëlecałą sztaturą i bestrim òpiórzenim. Jegò cało mô 60–72 cm długòscë<ref>Orta, J., Christie, D.A. & Garcia, E.F.J.: [https://www.hbw.com/species/red-kite-milvus-milvus Red Kite (Milvus milvus)]. [w:] del Hoyo, J., Elliott, A., Sargatal, J., Christie, D.A. & de Juana, E. (eds.). Handbook of the Birds of the World Alive.</ref>, a rozcygłosc jegò lecëdłów wënôszô 175–195 cm. Samce wôżą òd 800 do 1200 gramów, timczasã samice są wiãkszé i ważą midzë 1000 a 1300 gramów. Pòza różnicą we wôdze, midzë płcama ptôcha ni ma wiãkszich różniców - òbie pëłce są szlachòwno ùbarwióné – dërdzawé cało z cemniészima plecama i biôłima akcentama na lëcadłach. Ògón czerwònobruny z wiérzchù i jasnoszari òd spòdu, dosc mòcno wcãti (niżóden eùropejsczi drapieżnik ni mô tak mòcnégò wcãcô jak kania). Temù téż ògón je nôbarżi rozpòznôwalną znanką tegò ptôcha w loce na wiôldżich wësokòscach. Głowa, szëja i place òbsadzeniô skrzidłów jasnopòpielaté ze sztrichòwanim. Wiérzch cała dërdzawòbruny, spódk cała jasniészi, sztrichòwóny. Na dólny starnie skrzidła to dô wiôldżi biôłi plachc, na wiérzchny starnie przeznórtny pasyk. Żôłti dzób z czôrnym kùńcã, żółté nodżi. Rzôdkò zdôrzają sã ptôchë z [[Leùcyzm|leùcysticzną]] fòrmą. Szlachùjącô za nią czôrnô kania je përznã mniészô, mô wiele cemniészé òpiórzenié i mni wcãti ògón. W przërównanim do [[Mësznik|mësznika]] je wiele wikszô i mô dłëższé skrzidła. Pò lãgòwim czasu kanie mògą noclegòwac w karnach dochôdającëch nawetka do sta sztëk, chòc nie je tak drësznô jak czôrnô kania.
Lësatô kania czãsto lecy na prostëch abò letkò zdżãtëch skrzidłach, co w sztôłce przëbacziwô lëtrã M. W chùtczim i chwatczim loce mô prawie wiedno rozkòscérzony ògón, chtëren czasã sã przekrzéwiô.
Kanie żëją kòl 25 lat, chòc w 2023 we [[Wiôlgô Britaniô|Wiôldżi Britanii]] òstała nalazłô kania, chtërna dożëła 29 lat.<ref>[https://www.bbc.co.uk/news/uk-england-oxfordshire-65646200 "One of the first red kites released into Thames Valley dies"]. BBC News. 20 May 2023. Retrieved 31 July 2023.</ref>
== Biotop i jôda ==
[[Òbrôzk:Red Kite R1 4513.jpg|mały|Lësatô kania w loce, [[Wiôlgô Britaniô]]]]
=== Biotop ===
Widné, lëstowaté i mieszóné, stôré lasë roscącé kòl łąków i pól, czãstò w òkòlim dolëznów rzék, stawów, czë skalëstëch ùbrzegòw. Pòtikónô je na nizynnëch terenach, jak téż w górach.
=== Jôda ===
Kanie są generalisticznyma drapieżnikama i scërzożércama, przez co jich dieta je tak różnorodnô, jak jich strzodowiszcze. Pòżiwiają sã môłima [[Krãgówce|krãgówcama]], przede wszëtczim drobnyma [[Sysczi|syskama]], taczima jak: [[Toczk|toczczi]], czë [[Grëzôk|grëzôczi]], ale téż [[Wieszczórka|wieszczórczi]], [[Òwôd|òwadë]] i [[Wãdzëbôk|wãdzëbôczi]]. Zdarziwô jima sã téż jesc rëbë wëszmërgniãté na sztrąd. Ptôchë te baro sprôwnie zbiérają òb czas nisczégò lotu, dzãka czemù jachtëją na swòje òfiarë. Rozmieją nawetka wëcygnąc biérno płëwającé na wòdze rebë.
[[Òbrôzk:Rote Milane mit Beute.jpg|lewo|mały|Pôra kaniów z òfiarą, [[Niemieckô]]]]
Òne wòlą chwacëc żëwą zdobëcz, niż jesc scërza. Zdôrzô jim sã atakòwac jinszé rabùszné ptôchë (nawetka te wiãkszé òd nich) w célu kradzeżë jichny òfiarë, czim badérowie dolmaczą nalôżóné w ji wnãtrznoscach resztczi zwiérzãt, jaczich kanie ni mògłebë ùpòlowac.
Swòjich òfiarów wëzérają z gałãzów drzéwiãt, chòc òwadë i môłé sysczi rozmieje schwacëc nawetka òb czas wòlnégò chòdzeniô. Pòza tim lësaté kanie czasã plundrëją gniôzda [[Krëkòwaté|krëkòwatëch]].
== Lãgòwi cząd ==
[[Òbrôzk:Horst des Rotmilans (Milvus milvus).JPG|mały|Gniôzdo kani]]
Kanie rozmnôżają sã, czej staną sã dwa lata stôré, chòc zdôrzają sã przëtrôfczi roczniôków, jaczim ùdało sã młodzëc<ref>Evans, I. M.; Cordero, P. J.; Parkin, D. T. (1998). "Successful breeding at one year of age by Red Kites Milvus milvus in southern England". Ibis. 140 (1): 53–57</ref>. Są to mònogamiczné ptôchë, chtërne w stałëch pòpùlacjach òstają w ti sami pôrze przez zëmã, òsoblëwò jeżlë ptôchë òstają na tim samim teritorium. Wãdrowné kanie pò zëmie wrôcają do tegò samégò gniôzda, czim pòdtrzimùją wiérnosc midzë ptôchama.
=== Gniôzdo ===
[[Òbrôzk:Milmil njg 980613.jpg|lewo|mały|Młodé kanie w gniôzdze. [[Berlëno]], [[Niemieckô]].]]
Nôczãscy bùdowóné je na lëstowatim drzewie, na rozwietwienim gałãzów abò twélów, na wësokòscë midzë 12 a 15 métrów. Kanie wic wëbiérają drzewa na kraju lasa. Bùdową gniôzda zajmùją sã òbie pëłce. Samc haléwô wietwie ò długòscë 30–50 cm, a samica je ukłôdô. Pòdstawą je grëbô gałąz, scelonô mitczima roscënama, ale téż różnyma smiecama, np. papiorã, kòdrama, gnôtama czë skórama. Òba ptôchë dodają wiãcy materiałów òb czas inkùbacji i chòwaniô młodëch. Kanie jedno gniôzdô ùżiwają przez wiele lat<ref name=":0">Cramp, Stanley; et al., eds. (1980). "Milvus milvus Red Kite". Handbook of the Birds of Europe the Middle East and North Africa: The Birds of the Western Palearctic. Vol. II: Hawks to Bustards. Oxford: Oxford University Press. pp. 36–44. <nowiki>ISBN 978-0-19-857505-4</nowiki>.</ref>. Òkazjonalnie zdôrzô sã kanióm wëkòrzëstac stôré gniôzda [[Mësznik|mëszników]].
Òb czas lãgù kanie są agresywné wedle rabùsznëch ptôchów, tak tej w òkòlim jich gniôzda nie młodzą sã jinszé [[grabcaté]].
=== Jaja ===
[[Òbrôzk:Milvus milvus MHNT.ZOO.2010.11.80.15.jpg|mały|Jaje lësati kani]]
Kanie niesą sã leno rôz w rokù, midzë łżëkwiatã a majã<ref name=":0" />. Òb czas jednégò gòdowégò cządu kania niese wic midzë jednym, a trzema jajama, chòc òdnotowóné bëłë przëtrôfczi, gdze w lãgù bëło jaż piãc jôj. Jaja są biôłé z zemisto-czerwionyma kropkama. Statisticznie jaje wôżi 63 gramë i mô wëmiarë 57 mm x 47 mm.<ref name=":0" /> Jeżlë ląg òstónie stracóny, samica zôs zniese jaja.<ref name=":0" />
=== Wësadowanié i dorôstanié ===
Jaja wësadowóné są przez kòl 30 dni przez samicã. Samc w tim czasu przënosy ji jôdã, chòc czasã zastãpùje swòją partnerkã, czej òna lecy jachtowac. Pizglãta pierszé dwa tidzenié żëcô spãdzywają w gniôzdze razã z nënką, chtërna kôrmi je jôdą halóną przez samca. Pózni òba ptôchë lôtają pò jestkù dlô młodëch. Pò 14 dniach młodé same mùszą sã karmic przëniosłim do gniôzda jedzenim. Kòl 45 dnia żëcô pizglãta zaczinają sã rãplowac na gałãze kòl gniôzda, le rzôdkò pòdlatują przed 48–50 dniã żëcô, a czasã nawetka do wiekù 60–70 dniów. Pòdlatawce spãdzywają jesz dwa do trzech tidzeniów w pòblëżim gniôzda z dokarmiającyma jich starszima. Pò dwùch latach młodé stają sã dozdrzeniałima ptôchama.
== Galeriô ==
[[Òbrôzk:Leucistic Red Kite - Flickr - Andy Morffew.jpg|lewo|mały|Leùcysticzny òsobnik]]
[[Òbrôzk:Milhafre-real, Red Kite (52539996534).jpg|centruj|mały|Lësatô kania w Pòrtugalii]]
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
[[Kategòrëjô:Ptôchë]]
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Ptôchë Eùropë]]
[[Kategòrëjô:Rabùszné Ptôchë]]
sr0wg6mtbt6o9m42be56xne6g2jpatu
204498
204492
2026-07-06T11:16:10Z
Iketsi
3254
Galeriô
204498
wikitext
text/x-wiki
'''Lësatô kania''' (''Milvus milvus'') – gatënk drapieżnégò ptôcha z rodzëznë [[Accipitridae|jastrzëbòwatëch]]. Gatënk ten dzysdnia gniôzdëje w Eùropie, chòc w dôwnoce rozmnôżôł sã téż w zôpadny Azji i północno-zôpadny Africe<ref>BirdLife International (2020). "Milvus milvus". IUCN Red List of Threatened Species. 2020 e.T22695072A181651010. doi:10.2305/IUCN.UK.2020-3.RLTS.T22695072A181651010.en. Retrieved 19 November 2021.</ref>.
== Systematika ==
[[Òbrôzk:Lunak_cerveny.jpg|mały|Dozdrzeniałô kania]]
Lësatô kania òstała òpisónô przez szwedzczégò przërodnika [[Karol Linneùsz|Karola Linneùsza]] w 1758 r., w dzesąti edycje jegò dokazu ''Systema Naturae'' pòd binominalną gatënkòwą pòzwą ''Falco milvus''<ref>[[Karol Linneùsz|Linneùsz, Karol]] (1758) [https://www.biodiversitylibrary.org/page/726996#page/106/mode/1up Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata] (w łacëznie), Tom pierszi, Vol. 1. Stockholm: Laurentius Salvius. p. 89. "F. cera flava, cauda forsicata, corpore ferrugineo, capite albidiore."</ref>. Słowò ''Milvus'' je łacyńską pòzwą na tegò ptôcha. W 1799 francësczi przëodnik Bernard Germain de Lacépède przeniósł gatënk do rodz''aju Milvus.''
Wëapartniwóné są dwa pòdgatënczi Milvus milvus
* ''M. milvus milvus'' <small>(Linnaeus, 1758)</small> – '''lësatô kania.'''
* ''M. milvus fasciicauda'' <small>E. Hartert, 1914</small> – '''zelonogłowskô kania''' – endemit [[Òstrowë Zelony Głowë|Òstrowów Zelony Głowë]], prawie na gwës wëmarłi.
W rodzaju ''Milvus'' są téż dwa jinszé gatënczi: czôrnô kania i egipskô kania. Znóné są przëtrôfczi ùdóny hibridizacji midzë lësatima i czôrnyma kaniama.
=== Hibridizacjô ===
[[Òbrôzk:Obernberg am Inn Adlerwarte Rotmilan-0136.jpg|mały|250x250px|Lësatô kania, [[Aùstriô]]]]
Rodzôj ''Milvus'' zawiérô w se jesz dwa jinszé gatënczi - czôrną kaniã i egipską kaniã<ref>Gill, Frank; Donsker, David, eds. (2018). [http://www.worldbirdnames.org/bow/raptors/ "New World vultures, Secretarybird, kites, hawks, eagles]". World Bird List Version 8.2. International Ornithologists' Union. Retrieved 24 September 2018.</ref>. Są znóné przëtrôfczi, w jaczich lësatim kanióm ùdało sã rozmnożëc z czôrnyma kaniama, dzało sã to tak w niewòli, jak i dzëczim stanie, np. na Òstrowach Zelony Głowë.
Strefa hibridizacje w jaczi òba gatënczi òkazyjnie sã krziżëją, jistnieje w Centralny Eùropie.<ref>Heneberg, Petr; Dolinay, Matej; Matuší, Hynek; Pfeiffer, Thomas; Nachtigall, Winfried; Bizos, Jiri; Šimčiková, Daniela; Literák, Ivan (2016). [https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0159202 "Conservation of the Red Kite Milvus milvus (Aves: Accipitriformes) Is Not Affected by the Establishment of a Broad Hybrid Zone with the Black Kite Milvus migrans migrans in Central Europe"]. PLoS ONE. 11 (7). Retrieved 30 August 2025.</ref> Geneticzné badérowaniô kaniów z te òbrëmiô nie wëkôzałë niżódnégò [[mtDNA]] czôrnëch kaniów w lësatëch kaniach i vice versa. Przez to, że ten ôrt DNA je przekazéwóny przez nënczi, aùtorzë badaniô sùgerëją, że miészóné samice są bezpłodné zgòdą z [[Prawò Haldana|prawã Haldana]].
W 2006 rokù w Szkòcji doszło do ùdónégò rozrodu samca czôrny kanie z samicą lësati kanie w jaczim ùdało jim sã wëchòwac dwa pòdlatawce.
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus distr.png|lewo|mały|Apfelzynowi – lãgòwi cząd; mòdri – zëmòwanié; zelony – całoroczné wëstãpòwanié]]
Lësaté kanie są ptôchama, jaczé zamieszkùją lëstowaté lasë, łączi, pòla, dolëznë i sapòwaté pògrańcza. Pòchòdzą z zôpadny [[Palearkticznô krôjna|Palearkticzny krôjnë]], tak tej wszëtczé znóné rodné parë (jaczich je kòl 32,200–37,000) młodzą sã w Eùropie.
Przede wszëtczim żëje w zôpadny, westrzédny i pôłniowi Eùropie, nielëcznie téż we wschódnym parce Azji Mniészi i w północny Africe, gdze sztatus nie je gwësny - lãgòwô pòpùlacjô z Maroka je ùznôwónô za wëmarłą, chòc same kanie dali zëmùją w tim kraju. Dôwni gatënk szło pòtkac téż na Kanarijsczich Òstrowach i Òstrowach Zelony Głowë, gdze prawie na gwës wëmarł<ref>Garcia-del-Rey, E. (2011). Field Guide to the Birds of Macaronesia. Azores, Madeira, Canary Islands, Cape Verde. Lynx Edicions, Barcelona, Spain.</ref>. Kania je wãdrownô, chòc to zanôlégô òd ji pòpùlacji - ptôchë z pôłniô mieszkają ù se całi rok, a te żëjącé w ùmiarkòwónym klimace migrëją òd zélnikã do séwnikã nad [[Strzódzemné Mòrze]], abë wrócëc do swòjich gniôzd kòl strëmiannika. W przërównanim do [[Czôrnô kania|czôrny kanie]], ji syg je ògraniczony.
W Pòlsce je to nielëczny lãgòwi ptôch. Kanie są pòtikóné blós na zôpadze i nordowim wschòdze kraju ([[Wiôlgôpòlskô]], [[Pòmòrskô|Pòmòrzé]], nordowi part [[Dólny Szląsk|Dólnégò Szląska]] a téż [[Warmiô]] i [[Mazurë]])<ref>Ludwik Tomiałojć, Tadeusz Stawarczyk: Awifauna Polski. Rozmieszczenie, liczebność i zmiany. Wrocław: PTPP "pro Natura", 2003, s. 203. [[:pl:Specjalna:Książki/839196261X|ISBN 83-919626-1-X.]]</ref> W zaòstałëch regionach je rzôdkô, widiwónô je òb czas migracji. Niechtërne kanie w Centralny Eùropie mògą zëmòwac kòl rakòwiszczów lub jinëch môlów bòkadnëch w jôdã, le zachòwanié to je nieregùlarné i robią to leno pòjedinczé òsobniczi<ref>Marcin Karetta: Atlas ptaków. Pascal, 2010. I[[:pl:Specjalna:Książki/9788375136555|SBN 978-83-7513-655-5.]]</ref>.
== Znanczi gatënkù ==
[[Òbrôzk:Schwanz-Milane.jpg|mały|Ògònë kaniów: pò prawi czôrnô kania, na lewą – lësatô]]
[[Òbrôzk:Red Kite (Milvus milvus) skull at the Royal Veterinary College anatomy museum.JPG|mały|Czaszka ''Milvus Milvus'']]
Ptôch charakterizëje sã wëlecałą sztaturą i bestrim òpiórzenim. Jegò cało mô 60–72 cm długòscë<ref>Orta, J., Christie, D.A. & Garcia, E.F.J.: [https://www.hbw.com/species/red-kite-milvus-milvus Red Kite (Milvus milvus)]. [w:] del Hoyo, J., Elliott, A., Sargatal, J., Christie, D.A. & de Juana, E. (eds.). Handbook of the Birds of the World Alive.</ref>, a rozcygłosc jegò lecëdłów wënôszô 175–195 cm. Samce wôżą òd 800 do 1200 gramów, timczasã samice są wiãkszé i ważą midzë 1000 a 1300 gramów. Pòza różnicą we wôdze, midzë płcama ptôcha ni ma wiãkszich różniców - òbie pëłce są szlachòwno ùbarwióné – dërdzawé cało z cemniészima plecama i biôłima akcentama na lëcadłach. Ògón czerwònobruny z wiérzchù i jasnoszari òd spòdu, dosc mòcno wcãti (niżóden eùropejsczi drapieżnik ni mô tak mòcnégò wcãcô jak kania). Temù téż ògón je nôbarżi rozpòznôwalną znanką tegò ptôcha w loce na wiôldżich wësokòscach. Głowa, szëja i place òbsadzeniô skrzidłów jasnopòpielaté ze sztrichòwanim. Wiérzch cała dërdzawòbruny, spódk cała jasniészi, sztrichòwóny. Na dólny starnie skrzidła to dô wiôldżi biôłi plachc, na wiérzchny starnie przeznórtny pasyk. Żôłti dzób z czôrnym kùńcã, żółté nodżi. Rzôdkò zdôrzają sã ptôchë z [[Leùcyzm|leùcysticzną]] fòrmą. Szlachùjącô za nią czôrnô kania je përznã mniészô, mô wiele cemniészé òpiórzenié i mni wcãti ògón. W przërównanim do [[Mësznik|mësznika]] je wiele wikszô i mô dłëższé skrzidła. Pò lãgòwim czasu kanie mògą noclegòwac w karnach dochôdającëch nawetka do sta sztëk, chòc nie je tak drësznô jak czôrnô kania.
Lësatô kania czãsto lecy na prostëch abò letkò zdżãtëch skrzidłach, co w sztôłce przëbacziwô lëtrã M. W chùtczim i chwatczim loce mô prawie wiedno rozkòscérzony ògón, chtëren czasã sã przekrzéwiô.
Kanie żëją kòl 25 lat, chòc w 2023 we [[Wiôlgô Britaniô|Wiôldżi Britanii]] òstała nalazłô kania, chtërna dożëła 29 lat.<ref>[https://www.bbc.co.uk/news/uk-england-oxfordshire-65646200 "One of the first red kites released into Thames Valley dies"]. BBC News. 20 May 2023. Retrieved 31 July 2023.</ref>
== Biotop i jôda ==
[[Òbrôzk:Red Kite R1 4513.jpg|mały|Lësatô kania w loce, [[Wiôlgô Britaniô]]]]
=== Biotop ===
Widné, lëstowaté i mieszóné, stôré lasë roscącé kòl łąków i pól, czãstò w òkòlim dolëznów rzék, stawów, czë skalëstëch ùbrzegòw. Pòtikónô je na nizynnëch terenach, jak téż w górach.
=== Jôda ===
Kanie są generalisticznyma drapieżnikama i scërzożércama, przez co jich dieta je tak różnorodnô, jak jich strzodowiszcze. Pòżiwiają sã môłima [[Krãgówce|krãgówcama]], przede wszëtczim drobnyma [[Sysczi|syskama]], taczima jak: [[Toczk|toczczi]], czë [[Grëzôk|grëzôczi]], ale téż [[Wieszczórka|wieszczórczi]], [[Òwôd|òwadë]] i [[Wãdzëbôk|wãdzëbôczi]]. Zdarziwô jima sã téż jesc rëbë wëszmërgniãté na sztrąd. Ptôchë te baro sprôwnie zbiérają òb czas nisczégò lotu, dzãka czemù jachtëją na swòje òfiarë. Rozmieją nawetka wëcygnąc biérno płëwającé na wòdze rebë.
[[Òbrôzk:Rote Milane mit Beute.jpg|lewo|mały|Pôra kaniów z òfiarą, [[Niemieckô]]]]
Òne wòlą chwacëc żëwą zdobëcz, niż jesc scërza. Zdôrzô jim sã atakòwac jinszé rabùszné ptôchë (nawetka te wiãkszé òd nich) w célu kradzeżë jichny òfiarë, czim badérowie dolmaczą nalôżóné w ji wnãtrznoscach resztczi zwiérzãt, jaczich kanie ni mògłebë ùpòlowac.
Swòjich òfiarów wëzérają z gałãzów drzéwiãt, chòc òwadë i môłé sysczi rozmieje schwacëc nawetka òb czas wòlnégò chòdzeniô. Pòza tim lësaté kanie czasã plundrëją gniôzda [[Krëkòwaté|krëkòwatëch]].
== Lãgòwi cząd ==
[[Òbrôzk:Horst des Rotmilans (Milvus milvus).JPG|mały|Gniôzdo kani]]
Kanie rozmnôżają sã, czej staną sã dwa lata stôré, chòc zdôrzają sã przëtrôfczi roczniôków, jaczim ùdało sã młodzëc<ref>Evans, I. M.; Cordero, P. J.; Parkin, D. T. (1998). "Successful breeding at one year of age by Red Kites Milvus milvus in southern England". Ibis. 140 (1): 53–57</ref>. Są to mònogamiczné ptôchë, chtërne w stałëch pòpùlacjach òstają w ti sami pôrze przez zëmã, òsoblëwò jeżlë ptôchë òstają na tim samim teritorium. Wãdrowné kanie pò zëmie wrôcają do tegò samégò gniôzda, czim pòdtrzimùją wiérnosc midzë ptôchama.
=== Gniôzdo ===
[[Òbrôzk:Milmil njg 980613.jpg|lewo|mały|Młodé kanie w gniôzdze. [[Berlëno]], [[Niemieckô]].]]
Nôczãscy bùdowóné je na lëstowatim drzewie, na rozwietwienim gałãzów abò twélów, na wësokòscë midzë 12 a 15 métrów. Kanie wic wëbiérają drzewa na kraju lasa. Bùdową gniôzda zajmùją sã òbie pëłce. Samc haléwô wietwie ò długòscë 30–50 cm, a samica je ukłôdô. Pòdstawą je grëbô gałąz, scelonô mitczima roscënama, ale téż różnyma smiecama, np. papiorã, kòdrama, gnôtama czë skórama. Òba ptôchë dodają wiãcy materiałów òb czas inkùbacji i chòwaniô młodëch. Kanie jedno gniôzdô ùżiwają przez wiele lat<ref name=":0">Cramp, Stanley; et al., eds. (1980). "Milvus milvus Red Kite". Handbook of the Birds of Europe the Middle East and North Africa: The Birds of the Western Palearctic. Vol. II: Hawks to Bustards. Oxford: Oxford University Press. pp. 36–44. <nowiki>ISBN 978-0-19-857505-4</nowiki>.</ref>. Òkazjonalnie zdôrzô sã kanióm wëkòrzëstac stôré gniôzda [[Mësznik|mëszników]].
Òb czas lãgù kanie są agresywné wedle rabùsznëch ptôchów, tak tej w òkòlim jich gniôzda nie młodzą sã jinszé [[grabcaté]].
=== Jaja ===
[[Òbrôzk:Milvus milvus MHNT.ZOO.2010.11.80.15.jpg|mały|Jaje lësati kani]]
Kanie niesą sã leno rôz w rokù, midzë łżëkwiatã a majã<ref name=":0" />. Òb czas jednégò gòdowégò cządu kania niese wic midzë jednym, a trzema jajama, chòc òdnotowóné bëłë przëtrôfczi, gdze w lãgù bëło jaż piãc jôj. Jaja są biôłé z zemisto-czerwionyma kropkama. Statisticznie jaje wôżi 63 gramë i mô wëmiarë 57 mm x 47 mm.<ref name=":0" /> Jeżlë ląg òstónie stracóny, samica zôs zniese jaja.<ref name=":0" />
=== Wësadowanié i dorôstanié ===
Jaja wësadowóné są przez kòl 30 dni przez samicã. Samc w tim czasu przënosy ji jôdã, chòc czasã zastãpùje swòją partnerkã, czej òna lecy jachtowac. Pizglãta pierszé dwa tidzenié żëcô spãdzywają w gniôzdze razã z nënką, chtërna kôrmi je jôdą halóną przez samca. Pózni òba ptôchë lôtają pò jestkù dlô młodëch. Pò 14 dniach młodé same mùszą sã karmic przëniosłim do gniôzda jedzenim. Kòl 45 dnia żëcô pizglãta zaczinają sã rãplowac na gałãze kòl gniôzda, le rzôdkò pòdlatują przed 48–50 dniã żëcô, a czasã nawetka do wiekù 60–70 dniów. Pòdlatawce spãdzywają jesz dwa do trzech tidzeniów w pòblëżim gniôzda z dokarmiającyma jich starszima. Pò dwùch latach młodé stają sã dozdrzeniałima ptôchama.
== Galeriô ==
Òbrôzk:Leucistic Red Kite - Flickr - Andy Morffew.jpg|Leùcysticzny òsobnik
Òbrôzk:Milhafre-real, Red Kite (52539996534).jpg|Lësatô kania w Pòrtugalii
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
[[Kategòrëjô:Ptôchë]]
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Ptôchë Eùropë]]
[[Kategòrëjô:Rabùszné Ptôchë]]
0y9qa7d122r52829buax827xt6bb2cw
204499
204498
2026-07-06T11:16:26Z
Iketsi
3254
{{Kòntrola}}
204499
wikitext
text/x-wiki
'''Lësatô kania''' (''Milvus milvus'') – gatënk drapieżnégò ptôcha z rodzëznë [[Accipitridae|jastrzëbòwatëch]]. Gatënk ten dzysdnia gniôzdëje w Eùropie, chòc w dôwnoce rozmnôżôł sã téż w zôpadny Azji i północno-zôpadny Africe<ref>BirdLife International (2020). "Milvus milvus". IUCN Red List of Threatened Species. 2020 e.T22695072A181651010. doi:10.2305/IUCN.UK.2020-3.RLTS.T22695072A181651010.en. Retrieved 19 November 2021.</ref>.
== Systematika ==
[[Òbrôzk:Lunak_cerveny.jpg|mały|Dozdrzeniałô kania]]
Lësatô kania òstała òpisónô przez szwedzczégò przërodnika [[Karol Linneùsz|Karola Linneùsza]] w 1758 r., w dzesąti edycje jegò dokazu ''Systema Naturae'' pòd binominalną gatënkòwą pòzwą ''Falco milvus''<ref>[[Karol Linneùsz|Linneùsz, Karol]] (1758) [https://www.biodiversitylibrary.org/page/726996#page/106/mode/1up Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata] (w łacëznie), Tom pierszi, Vol. 1. Stockholm: Laurentius Salvius. p. 89. "F. cera flava, cauda forsicata, corpore ferrugineo, capite albidiore."</ref>. Słowò ''Milvus'' je łacyńską pòzwą na tegò ptôcha. W 1799 francësczi przëodnik Bernard Germain de Lacépède przeniósł gatënk do rodz''aju Milvus.''
Wëapartniwóné są dwa pòdgatënczi Milvus milvus
* ''M. milvus milvus'' <small>(Linnaeus, 1758)</small> – '''lësatô kania.'''
* ''M. milvus fasciicauda'' <small>E. Hartert, 1914</small> – '''zelonogłowskô kania''' – endemit [[Òstrowë Zelony Głowë|Òstrowów Zelony Głowë]], prawie na gwës wëmarłi.
W rodzaju ''Milvus'' są téż dwa jinszé gatënczi: czôrnô kania i egipskô kania. Znóné są przëtrôfczi ùdóny hibridizacji midzë lësatima i czôrnyma kaniama.
=== Hibridizacjô ===
[[Òbrôzk:Obernberg am Inn Adlerwarte Rotmilan-0136.jpg|mały|250x250px|Lësatô kania, [[Aùstriô]]]]
Rodzôj ''Milvus'' zawiérô w se jesz dwa jinszé gatënczi - czôrną kaniã i egipską kaniã<ref>Gill, Frank; Donsker, David, eds. (2018). [http://www.worldbirdnames.org/bow/raptors/ "New World vultures, Secretarybird, kites, hawks, eagles]". World Bird List Version 8.2. International Ornithologists' Union. Retrieved 24 September 2018.</ref>. Są znóné przëtrôfczi, w jaczich lësatim kanióm ùdało sã rozmnożëc z czôrnyma kaniama, dzało sã to tak w niewòli, jak i dzëczim stanie, np. na Òstrowach Zelony Głowë.
Strefa hibridizacje w jaczi òba gatënczi òkazyjnie sã krziżëją, jistnieje w Centralny Eùropie.<ref>Heneberg, Petr; Dolinay, Matej; Matuší, Hynek; Pfeiffer, Thomas; Nachtigall, Winfried; Bizos, Jiri; Šimčiková, Daniela; Literák, Ivan (2016). [https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0159202 "Conservation of the Red Kite Milvus milvus (Aves: Accipitriformes) Is Not Affected by the Establishment of a Broad Hybrid Zone with the Black Kite Milvus migrans migrans in Central Europe"]. PLoS ONE. 11 (7). Retrieved 30 August 2025.</ref> Geneticzné badérowaniô kaniów z te òbrëmiô nie wëkôzałë niżódnégò [[mtDNA]] czôrnëch kaniów w lësatëch kaniach i vice versa. Przez to, że ten ôrt DNA je przekazéwóny przez nënczi, aùtorzë badaniô sùgerëją, że miészóné samice są bezpłodné zgòdą z [[Prawò Haldana|prawã Haldana]].
W 2006 rokù w Szkòcji doszło do ùdónégò rozrodu samca czôrny kanie z samicą lësati kanie w jaczim ùdało jim sã wëchòwac dwa pòdlatawce.
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus distr.png|lewo|mały|Apfelzynowi – lãgòwi cząd; mòdri – zëmòwanié; zelony – całoroczné wëstãpòwanié]]
Lësaté kanie są ptôchama, jaczé zamieszkùją lëstowaté lasë, łączi, pòla, dolëznë i sapòwaté pògrańcza. Pòchòdzą z zôpadny [[Palearkticznô krôjna|Palearkticzny krôjnë]], tak tej wszëtczé znóné rodné parë (jaczich je kòl 32,200–37,000) młodzą sã w Eùropie.
Przede wszëtczim żëje w zôpadny, westrzédny i pôłniowi Eùropie, nielëcznie téż we wschódnym parce Azji Mniészi i w północny Africe, gdze sztatus nie je gwësny - lãgòwô pòpùlacjô z Maroka je ùznôwónô za wëmarłą, chòc same kanie dali zëmùją w tim kraju. Dôwni gatënk szło pòtkac téż na Kanarijsczich Òstrowach i Òstrowach Zelony Głowë, gdze prawie na gwës wëmarł<ref>Garcia-del-Rey, E. (2011). Field Guide to the Birds of Macaronesia. Azores, Madeira, Canary Islands, Cape Verde. Lynx Edicions, Barcelona, Spain.</ref>. Kania je wãdrownô, chòc to zanôlégô òd ji pòpùlacji - ptôchë z pôłniô mieszkają ù se całi rok, a te żëjącé w ùmiarkòwónym klimace migrëją òd zélnikã do séwnikã nad [[Strzódzemné Mòrze]], abë wrócëc do swòjich gniôzd kòl strëmiannika. W przërównanim do [[Czôrnô kania|czôrny kanie]], ji syg je ògraniczony.
W Pòlsce je to nielëczny lãgòwi ptôch. Kanie są pòtikóné blós na zôpadze i nordowim wschòdze kraju ([[Wiôlgôpòlskô]], [[Pòmòrskô|Pòmòrzé]], nordowi part [[Dólny Szląsk|Dólnégò Szląska]] a téż [[Warmiô]] i [[Mazurë]])<ref>Ludwik Tomiałojć, Tadeusz Stawarczyk: Awifauna Polski. Rozmieszczenie, liczebność i zmiany. Wrocław: PTPP "pro Natura", 2003, s. 203. [[:pl:Specjalna:Książki/839196261X|ISBN 83-919626-1-X.]]</ref> W zaòstałëch regionach je rzôdkô, widiwónô je òb czas migracji. Niechtërne kanie w Centralny Eùropie mògą zëmòwac kòl rakòwiszczów lub jinëch môlów bòkadnëch w jôdã, le zachòwanié to je nieregùlarné i robią to leno pòjedinczé òsobniczi<ref>Marcin Karetta: Atlas ptaków. Pascal, 2010. I[[:pl:Specjalna:Książki/9788375136555|SBN 978-83-7513-655-5.]]</ref>.
== Znanczi gatënkù ==
[[Òbrôzk:Schwanz-Milane.jpg|mały|Ògònë kaniów: pò prawi czôrnô kania, na lewą – lësatô]]
[[Òbrôzk:Red Kite (Milvus milvus) skull at the Royal Veterinary College anatomy museum.JPG|mały|Czaszka ''Milvus Milvus'']]
Ptôch charakterizëje sã wëlecałą sztaturą i bestrim òpiórzenim. Jegò cało mô 60–72 cm długòscë<ref>Orta, J., Christie, D.A. & Garcia, E.F.J.: [https://www.hbw.com/species/red-kite-milvus-milvus Red Kite (Milvus milvus)]. [w:] del Hoyo, J., Elliott, A., Sargatal, J., Christie, D.A. & de Juana, E. (eds.). Handbook of the Birds of the World Alive.</ref>, a rozcygłosc jegò lecëdłów wënôszô 175–195 cm. Samce wôżą òd 800 do 1200 gramów, timczasã samice są wiãkszé i ważą midzë 1000 a 1300 gramów. Pòza różnicą we wôdze, midzë płcama ptôcha ni ma wiãkszich różniców - òbie pëłce są szlachòwno ùbarwióné – dërdzawé cało z cemniészima plecama i biôłima akcentama na lëcadłach. Ògón czerwònobruny z wiérzchù i jasnoszari òd spòdu, dosc mòcno wcãti (niżóden eùropejsczi drapieżnik ni mô tak mòcnégò wcãcô jak kania). Temù téż ògón je nôbarżi rozpòznôwalną znanką tegò ptôcha w loce na wiôldżich wësokòscach. Głowa, szëja i place òbsadzeniô skrzidłów jasnopòpielaté ze sztrichòwanim. Wiérzch cała dërdzawòbruny, spódk cała jasniészi, sztrichòwóny. Na dólny starnie skrzidła to dô wiôldżi biôłi plachc, na wiérzchny starnie przeznórtny pasyk. Żôłti dzób z czôrnym kùńcã, żółté nodżi. Rzôdkò zdôrzają sã ptôchë z [[Leùcyzm|leùcysticzną]] fòrmą. Szlachùjącô za nią czôrnô kania je përznã mniészô, mô wiele cemniészé òpiórzenié i mni wcãti ògón. W przërównanim do [[Mësznik|mësznika]] je wiele wikszô i mô dłëższé skrzidła. Pò lãgòwim czasu kanie mògą noclegòwac w karnach dochôdającëch nawetka do sta sztëk, chòc nie je tak drësznô jak czôrnô kania.
Lësatô kania czãsto lecy na prostëch abò letkò zdżãtëch skrzidłach, co w sztôłce przëbacziwô lëtrã M. W chùtczim i chwatczim loce mô prawie wiedno rozkòscérzony ògón, chtëren czasã sã przekrzéwiô.
Kanie żëją kòl 25 lat, chòc w 2023 we [[Wiôlgô Britaniô|Wiôldżi Britanii]] òstała nalazłô kania, chtërna dożëła 29 lat.<ref>[https://www.bbc.co.uk/news/uk-england-oxfordshire-65646200 "One of the first red kites released into Thames Valley dies"]. BBC News. 20 May 2023. Retrieved 31 July 2023.</ref>
== Biotop i jôda ==
[[Òbrôzk:Red Kite R1 4513.jpg|mały|Lësatô kania w loce, [[Wiôlgô Britaniô]]]]
=== Biotop ===
Widné, lëstowaté i mieszóné, stôré lasë roscącé kòl łąków i pól, czãstò w òkòlim dolëznów rzék, stawów, czë skalëstëch ùbrzegòw. Pòtikónô je na nizynnëch terenach, jak téż w górach.
=== Jôda ===
Kanie są generalisticznyma drapieżnikama i scërzożércama, przez co jich dieta je tak różnorodnô, jak jich strzodowiszcze. Pòżiwiają sã môłima [[Krãgówce|krãgówcama]], przede wszëtczim drobnyma [[Sysczi|syskama]], taczima jak: [[Toczk|toczczi]], czë [[Grëzôk|grëzôczi]], ale téż [[Wieszczórka|wieszczórczi]], [[Òwôd|òwadë]] i [[Wãdzëbôk|wãdzëbôczi]]. Zdarziwô jima sã téż jesc rëbë wëszmërgniãté na sztrąd. Ptôchë te baro sprôwnie zbiérają òb czas nisczégò lotu, dzãka czemù jachtëją na swòje òfiarë. Rozmieją nawetka wëcygnąc biérno płëwającé na wòdze rebë.
[[Òbrôzk:Rote Milane mit Beute.jpg|lewo|mały|Pôra kaniów z òfiarą, [[Niemieckô]]]]
Òne wòlą chwacëc żëwą zdobëcz, niż jesc scërza. Zdôrzô jim sã atakòwac jinszé rabùszné ptôchë (nawetka te wiãkszé òd nich) w célu kradzeżë jichny òfiarë, czim badérowie dolmaczą nalôżóné w ji wnãtrznoscach resztczi zwiérzãt, jaczich kanie ni mògłebë ùpòlowac.
Swòjich òfiarów wëzérają z gałãzów drzéwiãt, chòc òwadë i môłé sysczi rozmieje schwacëc nawetka òb czas wòlnégò chòdzeniô. Pòza tim lësaté kanie czasã plundrëją gniôzda [[Krëkòwaté|krëkòwatëch]].
== Lãgòwi cząd ==
[[Òbrôzk:Horst des Rotmilans (Milvus milvus).JPG|mały|Gniôzdo kani]]
Kanie rozmnôżają sã, czej staną sã dwa lata stôré, chòc zdôrzają sã przëtrôfczi roczniôków, jaczim ùdało sã młodzëc<ref>Evans, I. M.; Cordero, P. J.; Parkin, D. T. (1998). "Successful breeding at one year of age by Red Kites Milvus milvus in southern England". Ibis. 140 (1): 53–57</ref>. Są to mònogamiczné ptôchë, chtërne w stałëch pòpùlacjach òstają w ti sami pôrze przez zëmã, òsoblëwò jeżlë ptôchë òstają na tim samim teritorium. Wãdrowné kanie pò zëmie wrôcają do tegò samégò gniôzda, czim pòdtrzimùją wiérnosc midzë ptôchama.
=== Gniôzdo ===
[[Òbrôzk:Milmil njg 980613.jpg|lewo|mały|Młodé kanie w gniôzdze. [[Berlëno]], [[Niemieckô]].]]
Nôczãscy bùdowóné je na lëstowatim drzewie, na rozwietwienim gałãzów abò twélów, na wësokòscë midzë 12 a 15 métrów. Kanie wic wëbiérają drzewa na kraju lasa. Bùdową gniôzda zajmùją sã òbie pëłce. Samc haléwô wietwie ò długòscë 30–50 cm, a samica je ukłôdô. Pòdstawą je grëbô gałąz, scelonô mitczima roscënama, ale téż różnyma smiecama, np. papiorã, kòdrama, gnôtama czë skórama. Òba ptôchë dodają wiãcy materiałów òb czas inkùbacji i chòwaniô młodëch. Kanie jedno gniôzdô ùżiwają przez wiele lat<ref name=":0">Cramp, Stanley; et al., eds. (1980). "Milvus milvus Red Kite". Handbook of the Birds of Europe the Middle East and North Africa: The Birds of the Western Palearctic. Vol. II: Hawks to Bustards. Oxford: Oxford University Press. pp. 36–44. <nowiki>ISBN 978-0-19-857505-4</nowiki>.</ref>. Òkazjonalnie zdôrzô sã kanióm wëkòrzëstac stôré gniôzda [[Mësznik|mëszników]].
Òb czas lãgù kanie są agresywné wedle rabùsznëch ptôchów, tak tej w òkòlim jich gniôzda nie młodzą sã jinszé [[grabcaté]].
=== Jaja ===
[[Òbrôzk:Milvus milvus MHNT.ZOO.2010.11.80.15.jpg|mały|Jaje lësati kani]]
Kanie niesą sã leno rôz w rokù, midzë łżëkwiatã a majã<ref name=":0" />. Òb czas jednégò gòdowégò cządu kania niese wic midzë jednym, a trzema jajama, chòc òdnotowóné bëłë przëtrôfczi, gdze w lãgù bëło jaż piãc jôj. Jaja są biôłé z zemisto-czerwionyma kropkama. Statisticznie jaje wôżi 63 gramë i mô wëmiarë 57 mm x 47 mm.<ref name=":0" /> Jeżlë ląg òstónie stracóny, samica zôs zniese jaja.<ref name=":0" />
=== Wësadowanié i dorôstanié ===
Jaja wësadowóné są przez kòl 30 dni przez samicã. Samc w tim czasu przënosy ji jôdã, chòc czasã zastãpùje swòją partnerkã, czej òna lecy jachtowac. Pizglãta pierszé dwa tidzenié żëcô spãdzywają w gniôzdze razã z nënką, chtërna kôrmi je jôdą halóną przez samca. Pózni òba ptôchë lôtają pò jestkù dlô młodëch. Pò 14 dniach młodé same mùszą sã karmic przëniosłim do gniôzda jedzenim. Kòl 45 dnia żëcô pizglãta zaczinają sã rãplowac na gałãze kòl gniôzda, le rzôdkò pòdlatują przed 48–50 dniã żëcô, a czasã nawetka do wiekù 60–70 dniów. Pòdlatawce spãdzywają jesz dwa do trzech tidzeniów w pòblëżim gniôzda z dokarmiającyma jich starszima. Pò dwùch latach młodé stają sã dozdrzeniałima ptôchama.
== Galeriô ==
Òbrôzk:Leucistic Red Kite - Flickr - Andy Morffew.jpg|Leùcysticzny òsobnik
Òbrôzk:Milhafre-real, Red Kite (52539996534).jpg|Lësatô kania w Pòrtugalii
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
{{Kòntrola}}
[[Kategòrëjô:Ptôchë]]
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Ptôchë Eùropë]]
[[Kategòrëjô:Rabùszné Ptôchë]]
mnc5le8hwglza6hln19ne6zowtb0cst
Szablóna:Miono
10
10527
204441
173972
2026-07-05T12:00:06Z
Terrus38
14026
204441
wikitext
text/x-wiki
<br /><div style="text-align: center; width: 60%; margin: auto; padding: 1em; border: {{{border|solid 2px black}}}; background-color: {{{bgcolor|gold}}}; color: {{{fgcolor|black}}}; font-weight: bold;">[[File:Coat_of_arms_of_Kaszubians.png|50px|left]] Jeżlë wiész, że na môlëzna mô téż swòje [[Wiki:Normalizacëjô kaszëbsczi terminologiji|znormalizowóné kaszëbsczé miono]], òstawi òdkôz na starnie diskùsje. Nie zabôcz pòdac zdrzódło ti jinfòrmacje!</span></div><br />
slv7f3473hlrdndhvip341wgtrkf6ec
Charnowò
0
10835
204513
198573
2026-07-06T11:58:05Z
Iketsi
3254
[[Pòlsczi jãzëk|pòl.]]→{{jãz.|pl}}; ' – to je '→' – '
204513
wikitext
text/x-wiki
[[Òbrôzk:Kosciol6- Charnowo.jpg|mały|Kòscół w Charnowie]]
'''Charnowò''' ({{jãz.|pl}} ''Charnowo'') – [[wies]] w [[Gmina Ùszcz|Gminie Ùszcz]], w [[Stôłpsczi kréz|stołpsczim krézu]] [[pòmòrsczé wòjewództwò|pòmòrsczégò wòjewództwa]]. Tu je kòscół, a dzece w szkòle ùczą sã [[Kaszëbsczi jãzëk|kaszëbsczégò jãzëka]].
== Bùtnowé lënczi ==
{{Commons}}
* [http://dir.icm.edu.pl/pl/Slownik_geograficzny/Tom_I/548 ''Charnowo'' w „Geògraficznym słowôrzu Pòlsczégò Królestwa i jinëch słowiańsczich krajów”]
* [https://web.archive.org/web/20160116163609/http://spcharnowo.edu.pl/ szkòła]
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Gmina Ùszcz]]
pymqszyngdr4a455r7xkkyav9v5bjc1
Milvus
0
11187
204482
203358
2026-07-05T19:34:56Z
Rabùszniczka
19710
204482
wikitext
text/x-wiki
[[Òbrôzk:Red Kite and Black Kite NAUMANN.jpg|mały]]
[[Òbrôzk:Milvus migrans front(ThKraft).jpg|mały|Czôrnô kania w loce. ]]
'''Milvus'''<ref>[[Milvus]], [w:] Integrated Taxonomic Information System (ang.).</ref>, '''kania''' – rodzôj drapieżnëch ptôchów z pòdrodzëznë jastrzibów (''[[Accipitrinae]]'') w rodzëznie jastrzibòwatëch (''[[Accipitridae]]).''
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus, Meyerode, Belgium 1.jpg|mały|Lësaté kanie w Belgii]]
Gatënczi òbejmòwóné przez rodzôj żëją Eùropie, Azji, Africe i Òceanii <ref>D.W. Winkler, S.M. Billerman & I.J. Lovette: Hawks, Eagles, and Kites (Accipitridae), version 1.0.. W: S.M. Billerman, B.K. Keeney, P.G. Rodewald & T.S. Schulenberg (red.): [https://birdsoftheworld.org/bow/home Birds of the World]. Ithaca, NY: Cornell Lab of Ornithology, 2020. DOI: [https://birdsoftheworld.org/bow/species/accipi1/1.0/introduction 10.2173/bow.accipi1.01.] [dostęp 2020-06-05]. (ang.).</ref>.
== Systematika ==
Rodzôj òbejmùje trze gatënczi:
* ''Milvus milvus'' ([[Karol Linneùsz|Linnaeus]], 1758) – [[Lësatô kania]]
* ''Milvus migrans'' ([[Boddaert]], 1783) – [[Czôrnô kania]]
* ''Milvus aegyptius ([[Gmelin]], JF, 1788) – [[Egipskô kania]]''
== Òbaczë téż ==
{{#categorytree:Jastrzibowaté|hideroot=on|mode=pages}}
== Przëpisë ==
{{Przëpisë}}
== Lëteratura ==
* [[Bernard Zëchta]]: Słownik gwar kaszubskich na tle kultury ludowej, Ossolineum, Wrocław - Warszawa - Kraków 1968, tom II, s. 296.
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Kanie]]
6fp42z82s5zioqwx9u42a0dqj3svyb3
204485
204482
2026-07-05T19:56:31Z
Rabùszniczka
19710
Etymòlogiô
204485
wikitext
text/x-wiki
[[Òbrôzk:Red Kite and Black Kite NAUMANN.jpg|mały|Czôrnô i lësatô kania.]]
[[Òbrôzk:Milvus migrans front(ThKraft).jpg|mały|Czôrnô kania w loce. ]]
'''Milvus'''<ref>[[Milvus]], [w:] Integrated Taxonomic Information System (ang.).</ref>, '''kania''' – rodzôj drapieżnëch ptôchów z pòdrodzëznë jastrzibów (''[[Accipitrinae]]'') w rodzëznie jastrzibòwatëch (''[[Accipitridae]]).''
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus, Meyerode, Belgium 1.jpg|mały|Lësaté kanie w Belgii]]
Gatënczi òbejmòwóné przez rodzôj żëją Eùropie, Azji, Africe i Òceanii <ref>D.W. Winkler, S.M. Billerman & I.J. Lovette: Hawks, Eagles, and Kites (Accipitridae), version 1.0.. W: S.M. Billerman, B.K. Keeney, P.G. Rodewald & T.S. Schulenberg (red.): [https://birdsoftheworld.org/bow/home Birds of the World]. Ithaca, NY: Cornell Lab of Ornithology, 2020. DOI: [https://birdsoftheworld.org/bow/species/accipi1/1.0/introduction 10.2173/bow.accipi1.01.] [dostęp 2020-06-05]. (ang.).</ref>.
== Systematika ==
Rodzôj òbejmùje trze gatënczi:
* ''Milvus milvus'' ([[Karol Linneùsz|Linnaeus]], 1758) – [[Lësatô kania]]
* ''Milvus migrans'' ([[Boddaert]], 1783) – [[Czôrnô kania]]
* ''Milvus aegyptius ([[Gmelin]], JF, 1788) – [[Egipskô kania]]''
=== Etymòlogiô pòzwë ===
* ''Milvus'': {{łac.|milvus}} „kania”.
* ''Ictinus'': gr. ικτιν ''iktin'' lub ικτινος ''iktinos'' „kania”. Tipòwi gatënk: ''Falco milvus'' {{small|Linnaeus, 1758}}.
* ''Hydroictinia'': gr. ὑδρο- ''hudro-'' „wòdny-”, od ὑδωρ ''hudōr'', ὑδατος ''hudatos'' „woda”; rodzaj ''Ictinia'' {{small|Vieillot, 1816}} (kanialuk). Tipòwi gatënk: ''Falco migrans'' {{small|Boddaert, 1783}}
== Òbaczë téż ==
{{#categorytree:Jastrzibowaté|hideroot=on|mode=pages}}
== Przëpisë ==
{{Przëpisë}}
== Lëteratura ==
* [[Bernard Zëchta]]: Słownik gwar kaszubskich na tle kultury ludowej, Ossolineum, Wrocław - Warszawa - Kraków 1968, tom II, s. 296.
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Kanie]]
44zv4dxmghla8grlphjjplkoxebri56
204487
204485
2026-07-05T20:25:50Z
Terrus38
14026
drobné
204487
wikitext
text/x-wiki
[[Òbrôzk:Red Kite and Black Kite NAUMANN.jpg|mały|Czôrnô i lësatô kania.]]
[[Òbrôzk:Milvus migrans front(ThKraft).jpg|mały|Czôrnô kania w loce. ]]
'''Milvus'''<ref>[[Milvus]], [w:] Integrated Taxonomic Information System (ang.).</ref>, '''kania''' – rodzôj drapieżnëch ptôchów z pòdrodzëznë jastrzëbów (''[[Accipitrinae]]'') w rodzëznie jastrzëbòwatëch (''[[Accipitridae]]).''
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus, Meyerode, Belgium 1.jpg|mały|Lësaté kanie w Belgii]]
Gatënczi òbejmòwóné przez rodzôj żëją w Eùropie, Azji, Africe i Òceanii <ref>D.W. Winkler, S.M. Billerman & I.J. Lovette: Hawks, Eagles, and Kites (Accipitridae), version 1.0.. W: S.M. Billerman, B.K. Keeney, P.G. Rodewald & T.S. Schulenberg (red.): [https://birdsoftheworld.org/bow/home Birds of the World]. Ithaca, NY: Cornell Lab of Ornithology, 2020. DOI: [https://birdsoftheworld.org/bow/species/accipi1/1.0/introduction 10.2173/bow.accipi1.01.] [dostęp 2020-06-05]. (ang.).</ref>.
== Systematika ==
Rodzôj òbejmùje trzë gatënczi:
* ''Milvus milvus'' ([[Karol Linneùsz|Linnaeus]], 1758) – [[Lësatô kania]]
* ''Milvus migrans'' ([[Boddaert]], 1783) – [[Czôrnô kania]]
* ''Milvus aegyptius ([[Gmelin]], JF, 1788) – [[Egipskô kania]]''
=== Etymòlogiô pòzwë ===
* ''Milvus'': [[Łacyńsczi jãzëk|łac.]] „kania”.
* ''Ictinus'': gr. ικτιν ''iktin'' lub ικτινος ''iktinos'' „kania”. Tipòwi gatënk: ''Falco milvus'' {{small|Linnaeus, 1758}}.
* ''Hydroictinia'': gr. ὑδρο- ''hudro-'' „wòdny-”, od ὑδωρ ''hudōr'', ὑδατος ''hudatos'' „woda”; rodzaj ''Ictinia'' {{small|Vieillot, 1816}} (kanialuk). Tipòwi gatënk: ''Falco migrans'' {{small|Boddaert, 1783}}
== Òbaczë téż ==
{{#categorytree:Jastrzibowaté|hideroot=on|mode=pages}}
== Przëpisë ==
{{Przëpisë}}
== Lëteratura ==
* [[Bernard Zëchta]]: Słownik gwar kaszubskich na tle kultury ludowej, Ossolineum, Wrocław - Warszawa - Kraków 1968, tom II, s. 296.
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Kanie]]
1j50myosat6u6jtz25wttl2jquguny3
204502
204487
2026-07-06T11:22:05Z
Iketsi
3254
[[Łacyńsczi jãzëk|łac.]]→{{jãz.|la}}
204502
wikitext
text/x-wiki
[[Òbrôzk:Red Kite and Black Kite NAUMANN.jpg|mały|Czôrnô i lësatô kania.]]
[[Òbrôzk:Milvus migrans front(ThKraft).jpg|mały|Czôrnô kania w loce. ]]
'''Milvus'''<ref>[[Milvus]], [w:] Integrated Taxonomic Information System (ang.).</ref>, '''kania''' – rodzôj drapieżnëch ptôchów z pòdrodzëznë jastrzëbów (''[[Accipitrinae]]'') w rodzëznie jastrzëbòwatëch (''[[Accipitridae]]).''
== Wëstãpòwanié ==
[[Òbrôzk:Milvus milvus, Meyerode, Belgium 1.jpg|mały|Lësaté kanie w Belgii]]
Gatënczi òbejmòwóné przez rodzôj żëją w Eùropie, Azji, Africe i Òceanii <ref>D.W. Winkler, S.M. Billerman & I.J. Lovette: Hawks, Eagles, and Kites (Accipitridae), version 1.0.. W: S.M. Billerman, B.K. Keeney, P.G. Rodewald & T.S. Schulenberg (red.): [https://birdsoftheworld.org/bow/home Birds of the World]. Ithaca, NY: Cornell Lab of Ornithology, 2020. DOI: [https://birdsoftheworld.org/bow/species/accipi1/1.0/introduction 10.2173/bow.accipi1.01.] [dostęp 2020-06-05]. (ang.).</ref>.
== Systematika ==
Rodzôj òbejmùje trzë gatënczi:
* ''Milvus milvus'' ([[Karol Linneùsz|Linnaeus]], 1758) – [[Lësatô kania]]
* ''Milvus migrans'' ([[Boddaert]], 1783) – [[Czôrnô kania]]
* ''Milvus aegyptius ([[Gmelin]], JF, 1788) – [[Egipskô kania]]''
=== Etymòlogiô pòzwë ===
* ''Milvus'': {{jãz.|la}} „kania”.
* ''Ictinus'': {{jãz.|gr}} ικτιν ''iktin'' lub ικτινος ''iktinos'' „kania”. Tipòwi gatënk: ''Falco milvus'' {{small|Linnaeus, 1758}}.
* ''Hydroictinia'': {{jãz.|gr}} ὑδρο- ''hudro-'' „wòdny-”, od ὑδωρ ''hudōr'', ὑδατος ''hudatos'' „woda”; rodzaj ''Ictinia'' {{small|Vieillot, 1816}} (kanialuk). Tipòwi gatënk: ''Falco migrans'' {{small|Boddaert, 1783}}
== Òbaczë téż ==
{{#categorytree:Jastrzibowaté|hideroot=on|mode=pages}}
== Przëpisë ==
{{Przëpisë}}
== Lëteratura ==
* [[Bernard Zëchta]]: Słownik gwar kaszubskich na tle kultury ludowej, Ossolineum, Wrocław - Warszawa - Kraków 1968, tom II, s. 296.
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Jastrzibowaté]]
[[Kategòrëjô:Kanie]]
2h9p1zveaev0kxcsnzov7bcu4aiiv0r
Wenezuelskô
0
11696
204457
201932
2026-07-05T14:49:15Z
DawnyTest
14843
204457
wikitext
text/x-wiki
{{Infobox-państwò
|gwôsné miono = República Bolivariana de Venezuela
|miono = Boliwiarańskô Repùblika Wenezueli
|miono-genitiw = Wenezueli
|fana = Flag of Venezuela.svg
|herb = Coat of arms of Venezuela.svg
|na karce = LocationVenezuela.svg
|mòtto =
|jãzëk = [[Szpańsczi jãzëk|Szpańsczi]]
|stolëca = Caracas
|fòrma państwa = repùblika
|wiéchrzëzna =912,050
|procent-wòdë =
|lëdztwò =31,250,306 | rok = 2024
|dëtk = Wenezuelsczi bolivar | kòd dëtka = VES
|czasowô cona =
|swiãto =5 lëpińca
|himn = Gloria al Bravo Pueblo<br>(Chwała bëlnëmu lëdowi) <center>[[File:United States Navy Band - Gloria al Bravo Pueblo.ogg]]</center>
|kòd = VEN, VE
|Internet = .ve
|telefón = +58|Prezydeńt=Delcy Rodríguez (p.ò.)|Premiéra=|Mònarcha=
|aùtowi kòd=YV}}
'''Wenezuelskô''' abò '''Wenezuelô''' (szp. ''Venezuela''), '''Boliwiarańskô Repùblika Wenezueli''' (szp: ''República Bolivariana de Venezuela'') je państwã w Pôłniowi Americe. Greńczë z [[Kolumbiô|Kolumbią]] (długosc grańcy 2341 km), [[Brazylskô|Brazylską]] (2137 km) i [[Gujana|Gujaną]] (789 km). Stolëca i nôwikszi gard to [[Caracas]]. W Caracas ùrodzył sã [[Simón Boliwar|Simón Bolívar]], ''El Libertador'', stolemni jenerôł i pôłniowò-amerikańsczi bòhater, od chtërnégò mô miono wenezuelsczi dëtk, bolivar. Nôrodné swiãto je 5 lëpińca, je to dzéń samòstójnoty<ref>[https://www.state.gov/releases/2025/07/message-to-the-venezuelan-people-on-independence-day Message to the Venezuelan People on Independence Day] [2025-08-19]</ref>. W Wenezueli żëją [[kapibara|kapibarë]].
Wiéchrzëzna to 912,050 km², lëdztwò: 31,250,306 w 2024<ref>[https://web.archive.org/web/20210531110017/https://www.cia.gov/the-world-factbook/countries/venezuela/ Venezuela] CIA World Factbook [2025-08-19]</ref>.
<gallery mode="packed" caption="Galeriô Wenezueli">
Simón Bolívar by Ricardo Acevedo Bernal.jpg|Simón Bolívar
PicoBolivar2.jpg|Pico Bolivar
2952-Danzas_Guanaguanare_de_Venezuela_no_Festival_folclorico_da_Coruña._(8200095256).jpg|Tuńc Guanaguanare
</gallery>
<gallery mode="packed">
Salto_del_Angel-Canaima-Venezuela03.JPG
2007_02_Capybaras_08.jpg|Kapibarë
</gallery>
<gallery mode="packed">
Tricolor.jpg|Sejm
A_las_puertas_del_cielo.JPG|Puertas del cielo
</gallery>
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
{{Amerika}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Pôłniowô Amerika]]
es10pikx5ss4xogo93wwlht6n4krmsw
204466
204457
2026-07-05T15:34:32Z
Orbitminis
18250
pisënk
204466
wikitext
text/x-wiki
{{Infobox-państwò
|gwôsné miono = República Bolivariana de Venezuela
|miono = Boliwiarańskô Repùblika Wenezueli
|miono-genitiw = Wenezueli
|fana = Flag of Venezuela.svg
|herb = Coat of arms of Venezuela.svg
|na karce = LocationVenezuela.svg
|mòtto =
|jãzëk = [[Szpańsczi jãzëk|Szpańsczi]]
|stolëca = Caracas
|fòrma państwa = repùblika
|wiéchrzëzna =912,050
|procent-wòdë =
|lëdztwò =31,250,306 | rok = 2024
|dëtk = Wenezuelsczi bolivar | kòd dëtka = VES
|czasowô cona =
|swiãto =5 lëpińca
|himn = Gloria al Bravo Pueblo<br>(Chwała bëlnëmu lëdowi) <center>[[File:United States Navy Band - Gloria al Bravo Pueblo.ogg]]</center>
|kòd = VEN, VE
|Internet = .ve
|telefón = +58|Prezydeńt=Delcy Rodríguez (p.ò.)|Premiéra=|Mònarcha=
|aùtowi kòd=YV}}
'''Wenezuelskô''' abò '''Wenezuelô''' (szp. ''Venezuela''), '''Boliwiarańskô Repùblika Wenezueli''' (szp: ''República Bolivariana de Venezuela'') je państwã w [[Pôłniowô Amerika|Pôłniowi Americe]]. Grańczë z [[Kolumbiô|Kolumbią]] (dłużawa grańcë 2341 km), [[Brazylskô|Brazylską]] (2137 km) i [[Gujana|Gujaną]] (789 km). Stolëca i nôwikszi gard to [[Caracas]]. W Caracas ùrodzył sã [[Simón Boliwar|Simón Bolívar]], ''El Libertador'', stolëmny jenerôł i pôłniowò-amerikańsczi heroj, òd chtërnégò mô miono wenezuelsczi dëtk, bolivar. Nôrodné swiãto je 5 lëpińca, je to dzéń samòstójnotë<ref>[https://www.state.gov/releases/2025/07/message-to-the-venezuelan-people-on-independence-day Message to the Venezuelan People on Independence Day] [2025-08-19]</ref>. W Wenezueli żëją [[kapibara|kapibarë]].
Wiéchrzëzna to 912,050 km², lëdztwò: 31,250,306 w 2024<ref>[https://web.archive.org/web/20210531110017/https://www.cia.gov/the-world-factbook/countries/venezuela/ Venezuela] CIA World Factbook [2025-08-19]</ref>.
<gallery mode="packed" caption="Galeriô Wenezueli">
Simón Bolívar by Ricardo Acevedo Bernal.jpg|Simón Bolívar
PicoBolivar2.jpg|Pico Bolivar
2952-Danzas_Guanaguanare_de_Venezuela_no_Festival_folclorico_da_Coruña._(8200095256).jpg|Tuńc Guanaguanare
</gallery>
<gallery mode="packed">
Salto_del_Angel-Canaima-Venezuela03.JPG
2007_02_Capybaras_08.jpg|Kapibarë
</gallery>
<gallery mode="packed">
Tricolor.jpg|Sejm
A_las_puertas_del_cielo.JPG|Puertas del cielo
</gallery>
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
{{Amerika}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Pôłniowô Amerika]]
tky57ovwfcne03qpdnc6o9fol1p7mj1
Kapibara
0
11697
204472
202928
2026-07-05T16:23:42Z
Orbitminis
18250
Dofùlowanié òd pòlsczi Wiczipedie, przëpisë, pisënk, encyklopedicznosc, galeriô, red.
204472
wikitext
text/x-wiki
[[Òbrôzk:Capivara(Hydrochoerus_hydrochaeris).jpg|mały|Kapibara (''Hydrochoerus hydrochaeris''), òna z dzecma]]
'''Kapibara''' ([[Guarańsczi jãzëk|guarani]]: ''Kapiÿva'', [[Łacyńsczi jãzëk|łac]]. ''Hydrochoerus hydrochaeris'') – zort [[Sysczi|sysków]] z [[Rodzëzna (biologiô)|pòdrodzëznë]] [[Kapibôrë|kapibôr]] (Hydrochoerinae) w òbrãbie rodzëznë kawiowôtëch (Caviidae). Nôwikszi grëzôk z rodzëznë kawiowôtëch, ''Caviidae'', mòże wôżëc do 65 kg. Òna żëje w [[Pôłniowô_Amerika|Pôłniowi Americe]] i żëwi sã roscënama.
== Geògraficzné rozplacowanié ==
Zort òbjimô gatënczi wëstãpùjącé w [[Strzédnô Amerika|Strzédnim]] ([[Panama]]) i [[Pôłniowô Amerika|Pôłniowim Americe]] ([[Kolumbiô]], [[Wenezuelskô|Wenezuelô]], [[Gujana]], [[Brazylskô|Brazyliô]], [[Ekwador]], [[Perú]], [[Bòliwiô]], [[Paragwaj]], [[Argentina]] i [[Urugwaj]])<ref>C.J. Burgin, D.E. Wilson, R.A. Mittermeier, A.B. Rylands, T.E. Lacher & W. Sechrest: [https://lynxnaturebooks.com/product/illustrated-checklist-of-the-mammals-of-the-world/ ''Illustrated Checklist of the Mammals of the World''] (kasz. ''Zòbrôzkòwónô lësta sysków swiata''). 1. part: Monotremata to Rodentia. Barcelona: Lynx Edicions, 2020, s. 552. [[:pl:Specjalna:Książki/9788416728343|ISBN 978-84-16728-34-3]]. (anielsczi).</ref><ref name=":0">T. Lacher, Jr: Family Caviidae (Cavies, Capybaras and Maras). W: D.E. Wilson, T.E. Lacher, Jr & R.A. Mittermeier: [[Handbook of the Mammals of the World|''Handbook of the Mammals of the World'']] ([[Kaszëbsczi jãzëk|kasz]]. ''Ùczbòwnik sysków swiata''). 6. part: Lagomorphs and Rodents I. Barcelona: Lynx Edicions, 2016, s. 436–437. [[:pl:Specjalna:Książki/9788494189234|ISBN 978-84-941892-3-4]]. ([[Anielsczi jãzëk|anielsczi]]).</ref><ref>D.E. Wilson & D.M. Reeder (red.): [https://www.departments.bucknell.edu/biology/resources/msw3/browse.asp?s=y&id=13400217 ''Hydrochoeris'']. [w:] ''Mammal Species of the World. A Taxonomic and Geographic Reference'' (Wëd. 3) [on-line]. Johns Hopkins University Press, 2005. [przëstãp 2026-07-05].</ref>.
== Mòrfòlogiô ==
Długòta cała (bez ògòna) 1025–1340 mm, długòta ògòna 10–20 mm, długòta slédny stopë 200–250 mm, długòta ùcha 60–70 mm; masa cała 35–65 kg<ref name=":0" />.
== Galeriô ==
<gallery mode="packed">
Òbrôzk:Capibara 1.jpg|mały|Kapibôrë jôdają trôwã
Òbrôzk:2007_11_21_02_sCaplinSwimming.jpg|mały|Kapibara baro dobrze ùmieje płëwac
</gallery>
== Przëpisë ==
<references />
== Bùtnowé lënczi ==
{{Commons}}{{Kòntrola}}
[[Kategòrëjô:Grëzôczi]]
thba5w7ywmjp8j7n1183hsx2j4vkyax
204474
204472
2026-07-05T18:52:53Z
Terrus38
14026
drobna kòrekta
204474
wikitext
text/x-wiki
[[Òbrôzk:Capivara(Hydrochoerus_hydrochaeris).jpg|mały|Kapibara (''Hydrochoerus hydrochaeris''), òna z dzecma]]
'''Kapibara''' ([[Guarańsczi jãzëk|guarani]]: ''Kapiÿva'', [[Łacyńsczi jãzëk|łac]]. ''Hydrochoerus hydrochaeris'') – zort [[Cëcónë|sysków]] z [[Rodzëzna (biologiô)|pòdrodzëznë]] [[Kapibôrë|kapibôr]] (Hydrochoerinae) w òbrãbie rodzëznë kawiowatëch (Caviidae). Nôwikszi grëzôk z rodzëznë kawiowatëch, ''Caviidae'', mòże ważëc do 65 kg. Òna żëje w [[Pôłniowô_Amerika|Pôłniowi Americe]] i żëwi sã roscënama.
== Geògraficzné rozplacowanié ==
Zort òbjimô gatënczi wëstãpùjącé we [[Westrzédnô Amerika|Westrzédny]] ([[Panama]]) i [[Pôłniowô Amerika|Pôłniowi Americe]] ([[Kolumbiô]], [[Wenezuelskô|Wenezuela]], [[Gujana|Gùjana]], [[Brazylskô|Brazyliô]], [[Ekwador]], [[Perú|Peru]], [[Bòliwiô]], [[Paragwaj]], [[Argentina]] i [[Ùrugwaj]])<ref>C.J. Burgin, D.E. Wilson, R.A. Mittermeier, A.B. Rylands, T.E. Lacher & W. Sechrest: [https://lynxnaturebooks.com/product/illustrated-checklist-of-the-mammals-of-the-world/ ''Illustrated Checklist of the Mammals of the World''] (kasz. ''Zòbrôzkòwónô lësta sysków swiata''). 1. part: Monotremata to Rodentia. Barcelona: Lynx Edicions, 2020, s. 552. [[:pl:Specjalna:Książki/9788416728343|ISBN 978-84-16728-34-3]]. (anielsczi).</ref><ref name=":0">T. Lacher, Jr: Family Caviidae (Cavies, Capybaras and Maras). W: D.E. Wilson, T.E. Lacher, Jr & R.A. Mittermeier: [[Handbook of the Mammals of the World|''Handbook of the Mammals of the World'']] ([[Kaszëbsczi jãzëk|kasz]]. ''Ùczbòwnik sysków swiata''). 6. part: Lagomorphs and Rodents I. Barcelona: Lynx Edicions, 2016, s. 436–437. [[:pl:Specjalna:Książki/9788494189234|ISBN 978-84-941892-3-4]]. ([[Anielsczi jãzëk|anielsczi]]).</ref><ref>D.E. Wilson & D.M. Reeder (red.): [https://www.departments.bucknell.edu/biology/resources/msw3/browse.asp?s=y&id=13400217 ''Hydrochoeris'']. [w:] ''Mammal Species of the World. A Taxonomic and Geographic Reference'' (Wëd. 3) [on-line]. Johns Hopkins University Press, 2005. [przëstãp 2026-07-05].</ref>.
== Mòrfòlogiô ==
Dłużawa cała (bez ògòna) 1025–1340 mm, dłużawa ògòna 10–20 mm, dłużawa slédny stopë 200–250 mm, dłużawa ùcha 60–70 mm; masa cała 35–65 kg<ref name=":0" />.
== Galeriô ==
<gallery mode="packed">
Òbrôzk:Capibara 1.jpg|Kapibarë jedzącé trôwã
Òbrôzk:2007_11_21_02_sCaplinSwimming.jpg|Płiwającô kapibara
</gallery>
== Przëpisë ==
<references />
== Bùtnowé lënczi ==
{{Commons}}{{Kòntrola}}
[[Kategòrëjô:Grëzôczi]]
rzdngc2jc4ol3xbhil4745rhxbi92a5
Brëkòwnik:Orbitminis
2
12315
204468
204405
2026-07-05T15:42:43Z
Orbitminis
18250
Anulowanié wersje [[Special:Diff/204405|204405]] aùtora [[Special:Contributions/Orbitminis|Orbitminis]] ([[User talk:Orbitminis|diskùsjô]]) teraz już wróciłem
204468
wikitext
text/x-wiki
{| class="wikitable"
!<div align="center" class="usermessage">'''[[Òbrôzk:Kashubian board on shop, Hel, Poland.jpg|bezramki|205x205px]]'''
----[[Brëkòwnik:Orbitminis/Zapisownik|'''<big>ZAPISOWNIK</big>''']] | '''<big>Ò MIE</big>''' | '''<big>[[Diskùsëjô brëkòwnika:Orbitminis|DISKÙSJÔ BRËKÒWNIKA]]</big>'''
</div>{{#babel:pl|en-2|de-1|csb-1}}
== CSB [[Òbrôzk:Icons-flag-csb.png|bezramki]] ==
[[Òbrôzk:Kashubiaball.PNG|lewo|bezramki|94x94px]]Mójn! Nazéwóm sã Adóm. Jô nie ùmiã bëlno gadac pò kaszëbskù :( Ga robiã edicje w górnosorbsczi Wikipedii, pòstrzegł, że w kaszëbsczi Wiczipedie mô mni articzlów, za bëlno krótczich articzlów ë zacht jinszëch. Wiãc pòstanowił ju robic, ùlepszëc ë zbògacëc kaszëbską Wiczipedią. Òd 26 gromicznika 2026 r. jem dolmôczã interfejsu w kaszëbsczim jãzëkù w TranslateWiki. [https://translatewiki.net/wiki/User:Orbitminis] Nieraza pòprawiã edicje lëdzy z mniészą znajemnotë kaszëbsczégò jãzëka (jô téż móm, le piszã bezzmiłkòwò) bezzmiłkòwò pò kaszëbskù. W procëmnoscë zacht brëkòwników ùżëwóm téż słowôrze, cobë ùtrzëmac z lepszą kwalitetã pisënkù. I niestetë, tak samò jinszi brëkòwnicë, ni jem nôzmôrt aktiwnym wiczipedëstã w kaszëbsczi Wiczipedie z przëczënë paùz i jinëch zachów. Jem Pòlôchã, ni Kaszëbã i jeden z nômłodszëch wiczipedëstów na całczi kaszëbsczi Wiczipedie. <div style="font-family:Maiandra GD, sans-serif; font-size: 10pt; text-align: left;"><div style="border:1px solid #ccc; background: #fff; border-right:3px solid #ccc; border-bottom:3px solid #ccc; text-align: center; padding:3px; float:right; font-size: smaller; line-height: 1.3; margin-right: 4px;"><div style="width:100%">[[{{CURRENTDAYNAME}}|<font color="black">{{CURRENTDAYNAME}}</font>]]</div><div style="font-size: x-large; width: 100%;">[[{{CURRENTDAY}} {{CURRENTMONTHNAMEGEN}}|<font color="black">{{CURRENTDAY}}</font>]]</div><div style="width: 100%;">[[{{CURRENTMONTHNAME}}|<font color="black">{{CURRENTMONTHNAMEGEN}}</font>]] '''[[{{CURRENTYEAR}}|<font color="black">{{CURRENTYEAR}}</font>]] r.'''</div><div style="background: #eeeeee; color: #000;">'''{{CURRENTTIME}}''' UTC</div><div style="background: #eeeeee; color: #000;">+2</div></div></div>
== PL [[Òbrôzk:Icons-flag-pl.png|bezramki|24x24px]] ==
Cześć! Nazywam się Adam. Ja nie umiem dobrze gadać po kaszubsku :( Gdy robię edycje w górnołużyckiej Wikipedii, zauważyłem, że w kaszubskiej Wikipedii ma mniej artykułów, za dużo krótkich artykułów i wiele innych. Więc postanowiłem już pracować, ulepszyć i wzbogacić kaszubską Wikipedię. Od 26 lutego 2026 r. jestem tłumaczem języka kaszubskiego w TranslateWiki. Nieraz poprawię edycje ludzi z mniejszą znajomością kaszubskiego (ja też mam, ale piszę poprawnie) poprawnie po kaszubsku. W przeciwieństwie wielu użytkowników korzystam także słowniki, żeby utrzymać z lepszą jakością pisowni. I niestety, tak jak inni użytkownicy, nie jestem najbardziej aktywnym wikipedystą w kaszubskiej Wikipedii z powodu przerw i innych rzeczy. Jestem Polakiem, nie Kaszubą i jeden z najmłodszych wikipedystów na całej kaszubskiej Wikipedii.
----
'''''<big>Kòżdi je geniuszã, le jeżlë òbsądzôsz rëbã pò ji mòcë wdrapëwónia sã na bómie, sprôwi nédżã żëcô wierzący, że je głëpô. – [[Albert Einstein]]</big>'''''
----[[Òbrôzk:HILLBLU lente.png|bezramki|26x26px]] UserScan''':''' <span class="plainlinks"> [https://xtools.wmflabs.org/pages/csb.wikipedia.org/Orbitminis ùsôdzoné] • [https://xtools.wmflabs.org/ec/?user=Orbitminis&project=csbwiki lëcznik] • [{{fullurl:Specjalnô:Rejestr|type=block&page=Brëkòwnik:{{urlencode:{{{1}}}}}}} blokadë] • [https://guc.toolforge.org/?user={{urlencode:{{{1|{{BASEPAGENAME}}}}}}}|globalny wkłôd] • [https://commons.wikimedia.org/w/index.php?title=Special%3ALog&type=upload&user={{urlencode:{{{1|{{BASEPAGENAME}}}}}}}&hide_thanks_log=1 wësłané lopczi]</span>
----'''<big>Artikle: 12 (csb) 100+ (swiat)</big>'''
----[[Zrzesznica Samòstójnëch Państw]], [[Wełtawa]], [[NATO]], [[Sedzëba]], [[Rzéka]], [[Bòsniacczi jãzëk]], [[II swiatowô wòjna]], [[Prezydeńt]], [[Kòsowò je serbsczé]], [[Friderik Chòpin]], [[United Artists]], [[Kaszëbskô Szwajcariô]]
----'''<big>Kategòrie: 3 (csb) 10+ (swiat)</big>'''
----[[:Kategòrëjô:Rzéczi|Rzéczi]], [[:Kategòrëjô:Historiô|Historiô]], [[:Kategòrëjô:Eùropejskô Ùniô|Eùropejskô Ùniô]]
----'''<big>Szablónë: 3 (csb) 10+ (swiat)</big>'''
----[[Szablóna:User ru-3]], [[Szablóna:Gduńsczi kréz]], [[Szablóna:NATO]]
----'''<big>Pòdzãkòwaniô: 23 (csb) 50+ (swiat)</big>'''
----[[Brëkòwnik:Iketsi|Iketsi]] (10+), [[Brëkòwnik:DawnyTest|DawnyTest]] (9), [[Brëkòwnik:Ясамойла|Ясамойла]] (1), [[Brëkòwnik:HaxelKaszëba|HaxelKaszëba]] (2)
----[[Òbrôzk:Traditional kashubian design example.png|bezramki|212x212px]]
== Mòje brëkòkôstczë ==
{{Brëkòkôstka|[[Òbrôzk:Firefox logo, 2019.svg|35px]]|Hewòtny brëkòwnik kòrzëstô z [[Mozilla Firefox|Firefoxa]].|tło1=#2584A1|tło2=#F58E27}}{{Brëkòkôstka|{{NUMBEROFARTICLES}}|Hewòtny brëkòwnik mëslë, że w kaszëbsczi Wikipedie mómë za mało articzlów.|tło1=#7E7CF2|tło2=#4CEB3F}}{{Brëkòkôstka|[[Òbrôzk:Adminmop-no.gif|35px]]|Hewòtny brëkòwnik nie je sprôwnikã kaszëbsczi Wikipedie, le czejbë chcesz, zalecóm ti dòmòcë.}}{{Brëkòkôstka|[[Òbrôzk:No barnstars.png]]|Hewòtny brëkòwnik ni przejmujã gwiôzdek dlô mie.|tło1=#FF7670|tło2=#70A0FF}}{{Brëkòkôstka|[[Image:Herb Polski.svg|41px]]|Nen brëkòwnik je [[Pòlskô|Pòlôch]].|tło1=#942929|tło2=#942929|ramka=#000000}}[[Òbrôzk:Wikipedia-logo-transparent-cropped.png|lewo|bezramki]]
|}
nrlj35xfsvyu29qfasd4iu71in6i08o
Brëkòwnik:Orbitminis/Zapisownik
2
12317
204470
204406
2026-07-05T16:19:46Z
Orbitminis
18250
Anulowanié wersje [[Special:Diff/204406|204406]] aùtora [[Special:Contributions/Orbitminis|Orbitminis]] ([[User talk:Orbitminis|diskùsjô]])
204470
wikitext
text/x-wiki
{| class="wikitable"
!<div style="float: center; border: 1px solid #000; margin: 1px;">
{| cellspacing="0" style=" background: #EFFFFF"
| style="font-size: 10pt; padding: 4pt; line-height: 1.25em; color: black; text-align: left;" | '''Witómë w mojim zapisowniku! Bôczënk: Hewòtny zapisownik służë sã do napisaniô nadchòdzącëch artiklów ë szablónów. Proszã ni editowac mojégò zapisownika! (ni jo)'''
----[[Òbrôzk:Sandbox.svg|lewo|bezramki|78x78px]]'''<big>ZAPISOWNIK [[Brëkòwnik:Orbitminis|Ò MIE]]</big>''' [[Òbrôzk:Debate, discussion.jpg|bezramki|195x195px]] '''<big>[[Diskùsëjô brëkòwnika:Orbitminis|DISKÙSËJÔ BRËKÒWNIKA]]</big>'''
|}</div>
=== Niskùńczony kaszëbsczi dolmaczënk articzla Himn ===
[[Òbrôzk:Arvid_Liljelund_-_Man_Singing_Hymn_-_A_I_187_-_Finnish_National_Gallery.jpg|mały|''"Człowiek'' ''spiéwô himn"'' aùtorstwa Arvida Liljelunda (1884)]]
'''Himn''' ({{Jãz.|gr}}. ὕμνος ''hýmnos'' – ''piesń pòchwalnô'') – ùroczëstô ë pòdniosłô piesń pòchwalnô, zwëczajno ò apòsztroficznym charakterze, kòmpònowanô na czesc bóżstwa, òsoblëwi òsobë, wëdarzeniô, tatczëznë (kraju), a negò téż ideji.
== Historiô ==
W kòle eùropejsczi kùlturë himnë znóne były w basenie Morza Śródziemnego już w starożytności. Na stałe weszły do kanonów kulturowych wraz z rozwojem chrześcijaństwa, który uczynił swoim ten rodzaj literacki wzorując się na hebrajskim Starym Testamencie i judeochrześcijańskim Nowym Testamencie.
=== Wëjimk articzla [[Infòrmatika]] do skaszëbieniô ===
Nôwprzódné pòspòdle tegò, co stało sã infòrmatiką, bëłë rëchli jak wënalezënk współczesnégò cyfrowégò kòmpùtra. Maszinë do robieniô stałëch lëczbòwëch zadaniów, taczi jak lëczbòwnica, bëłë znóné ju w starożetnoscë i pòmôgałë w lëczenium, na przikłôd w mnojeniu ë dzeleniu. Algòritmë do robieniô òbliczeniów bëłë brëkòwóné ju w starożetnoscë, jesz przed rozwitiém zaawansowonégò kòmpùtrovégò sprzãtu.
|}
o6gwj1rulf78eo9v3c1g4116429xg39
204471
204470
2026-07-05T16:23:03Z
Orbitminis
18250
do skùńczenia bò pòlskô Wiczipediô pò prôwdze je zadreżno zaawansowóny jak nie wiem co
204471
wikitext
text/x-wiki
{| class="wikitable"
!<div style="float: center; border: 1px solid #000; margin: 1px;">
{| cellspacing="0" style=" background: #EFFFFF"
| style="font-size: 10pt; padding: 4pt; line-height: 1.25em; color: black; text-align: left;" | '''Witómë w mojim zapisowniku! Bôczënk: Hewòtny zapisownik służë sã do napisaniô nadchòdzącëch artiklów ë szablónów. Proszã ni editowac mojégò zapisownika! (ni jo)'''
----[[Òbrôzk:Sandbox.svg|lewo|bezramki|78x78px]]'''<big>ZAPISOWNIK [[Brëkòwnik:Orbitminis|Ò MIE]]</big>''' [[Òbrôzk:Debate, discussion.jpg|bezramki|195x195px]] '''<big>[[Diskùsëjô brëkòwnika:Orbitminis|DISKÙSËJÔ BRËKÒWNIKA]]</big>'''
|}</div>
=== Niskùńczony kaszëbsczi dolmaczënk articzla [[Kapibara]] ===
== Systematiczny pòdzôł ==
Do rodzaju należą następujące występujące współcześnie gatënczi<ref name="mdd" /><ref name="amw" /><ref name="Burgin&inni" /><ref name="Cichocki&inni" />:
{| class="wikitable"
!Céchùnk
!Gatënk
!Aùtór i rok òpisënkù
!Zwëczajòwô pòzwa<ref name="Cichocki&inni" />
!Pòdgatënczi<ref name="Lacher" /><ref name="Burgin&inni" /><ref name="amw" />
!Geògraficzné rozplacowanié<ref name="Lacher" /><ref name="Burgin&inni" /><ref name="amw" />
!Spòdleczné wëmiarë<ref name="Lacher" /><ref name="amw" /><ref>'''DC''' – długość ciała; '''DO''' – długość ogona; '''MC''' – masa ciała</ref>
!Sztatus
[[Międzynarodowa Unia Ochrony Przyrody|IUCN]]<ref>{{cytuj stronę|url=https://www.iucnredlist.org/search?taxonomies=135300&searchType=species|tytuł=Taxonomy: ''Hydrochoerus'' – Genus|opublikowany=The IUCN Red List of Threatened Species|data dostępu=2025-01-06|język=en}}</ref>
|-
|[[Òbrôzk:Hydrochoeris_hydrochaeris_(capyvara)_at_the_Faunistic_Park_"La_Torbiera"_at_Agrate_Conturbia_village,_Piemonte_(Italy).jpg|centruj|150x150px]]
|''Hydrochoerus hydrochaeris''
|([[Karol Linneùsz|Linnaeus]], 1766)
|[[kapibara wielka]]
|[[Takson monotypowy|gatunek monotypowy]]
|na wschód od [[Andy|Andów]] w Kolumbii, Wenezueli, regionie [[Gujana (region)|Gujana]], Brazylii, Ekwadorze, Peru, Boliwii, Paragwaju, Urugwaju oraz północno-zachodniej i wschodniej Argentynie
|DC: 107–134 cm
DO: 1–2 cm MC: 35–65 kg
| align="center" |{{Colorbox|#53bc36|{{Kolor|#ffffff|'''LC'''}}}}
|-
|[[Òbrôzk:Hydrochoerus_isthmius.jpg|centruj|150x150px]]
|''Hydrochoerus isthmius''
|[[Edward Alphonso Goldman|E.A. Goldman]], 1912
|[[kapibara mała]]
|gatunek monotypowy
|wschodnia Panama, zachodnia Kolumbia i północno-zachodnia Wenezuela ([[Zulia]])
|DC: około 102 cm
DO: brak danych MC: brak danych
| align="center" |{{Colorbox|#c3c3c3|{{Kolor|#ffffff|'''DD'''}}}}
|}
Kategorie IUCN: {{Colorbox|#53bc36|{{Kolor|#ffffff|'''LC'''}}}} – [[gatunek najmniejszej troski]], {{Colorbox|#c3c3c3|{{Kolor|#ffffff|'''DD'''}}}} – [[Czerwona księga gatunków zagrożonych|gatunki o nieokreślonym stopniu zagrożenia]].
Opisano również gatunki wymarłe z [[Pliocen|pliocenu]] i [[Plejstocen|plejstocenu]]<ref name="zij" />:
* ''[[Hydrochoerus ballesterensis]]'' {{small|([[Carlos Rusconi|Rusconi]], 1934)}}<ref name="Rusconi" /> (Argentyna)
* ''[[Hydrochoerus gaylordi]]'' {{small|([[Ross Douglas Earle MacPhee|MacPhee]], [[Ronald Singer|Singer]] & [[Michael Diamond (paleontolog)|Diamond]], 2000)}}<ref name="mac&inni" /> ([[Grenada]]).
* ''[[Hydrochoerus hesperotiganites]]'' {{small|[[Richard White|R. White]], [[Jim Mead|Mead]], [[Gary Morgan|Morgan]] & [[Thomas A. Deméré|Deméré]], 2022}}<ref name="whi&inni" /> ([[Stany Zjednoczone]])
=== Wëjimk articzla [[Infòrmatika]] do skaszëbieniô ===
Nôwprzódné pòspòdle tegò, co stało sã infòrmatiką, bëłë rëchli jak wënalezënk współczesnégò cyfrowégò kòmpùtra. Maszinë do robieniô stałëch lëczbòwëch zadaniów, taczi jak lëczbòwnica, bëłë znóné ju w starożetnoscë i pòmôgałë w lëczenium, na przikłôd w mnojeniu ë dzeleniu. Algòritmë do robieniô òbliczeniów bëłë brëkòwóné ju w starożetnoscë, jesz przed rozwitiém zaawansowonégò kòmpùtrovégò sprzãtu.
|}
r4y70duofkmydvn7negt2pmf4cz1iyh
Diskùsëjô brëkòwnika:Orbitminis
3
12323
204442
202197
2026-07-05T12:50:46Z
Terrus38
14026
/* Òddzãkòwanié */ nowa sekcja
204442
wikitext
text/x-wiki
<div align="center" class="usermessage">Witôjtaż w mòji diskùsji brëkòwnika! Mòżesz téż pisac pò kaszëbskù, pò pòlskù, pò anielskù i pò miemiecckù, a pònemù bãdã òdpòwiadał.
----[[Òbrôzk:Sandbox.svg|lewo|bezramki|78x78px]][[Brëkòwnik:Orbitminis/Zapisownik|<big>ZAPISOWNIK</big>]] [[Brëkòwnik:Orbitminis|<big>Ò MIE</big>]] [[Òbrôzk:Debate,_discussion.jpg|bezramki|195x195px]] '''<big>DISKÙSËJÔ BRËKÒWNIKA</big>'''
</div>
<div align="center" class="disscusion">
== Poprawanie linków ==
Hej. Nie musisz wykonywać takich poprawek, jak <tt><nowiki>[[Gduńsk]]ù -> [[Gduńsk|Gduńskù]]</nowiki></tt> – w najbliższych dniach (może tygodniach) konfiguracja linków zostanie naprawiona na poziomie całej wiki. Pozdrawiam, [[Brëkòwnik:Tsca|tsca]] ([[Diskùsëjô brëkòwnika:Tsca|diskùsëjô]]) 21:01, 31 zél 2025 (CEST)
:Wiem, proszę pana. Ale ja muszę poprawiać kaszubską Wikipedię, żeby nie były takich błędów ortograficznych jak "[[Gduńsk]]ù" i "Jop Polsczei jaczieego", bo te błędy mogą sprawiać kaszubską Wikipedię jako encyklopedię trzeciego świata jak najmniejsze Wikipedie w różnych językach. I oczywiście te źródła, artykuły, samouczki i szablony niektóre wyglądają staro jak w latach 2000-2010, więc muszą mieć nowe źródła, świeże infomacje i szablony podobne do polskiej Wikipedii po kaszubsku. Więc przepraszam najmocniej za takie, które spełniłem. Pozdrawiam, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 10:12, 1 Séw 2025 (CEST)
== Automatycznie tłumaczone artykuły ==
Wklejanie automatycznie tłumaczonych artykułów mija się z celem. Skoro, jak sam deklarujesz na swojej stronie użytkownika, nie znasz języka kaszubskiego, nie jesteś w stanie zauważyć ani poprawić błędów w maszynowo przetłumaczonych treściach. Hasła utworzone w ten sposób niestety muszą zostać usunięte. [[Brëkòwnik:Tsca|tsca]] ([[Diskùsëjô brëkòwnika:Tsca|diskùsëjô]]) 23:28, 29 ruj 2025 (CET)
:czemu? [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 16:11, 18 lës 2025 (CET)
::CSB: Mòjn panie Tsca! Dzysë jô sã dowiedzôł twòją nôùczkã, żëbë nie wlëmic "aùtomaticznie dolmaczonëch artiklów". Wiãc pòstanowił ùżëc słowôrza do stwórzbë artiklów, żëbë bëło trochã lepszé artiklów z pasëjącą pisënką. Żle w mojich artiklach nalézają sã felë, dôj mni znac. Pozdrôwióm, Orbitminis.
::PL: Cześć, panie Tsca! Dzisiaj ja się dowiedziałem twoją nauczkę, żeby nie wklejać „automatycznie tłumaczonych artykułów”. Więc postanowiłem użyć słownika do stworzenia artykułów, żeby było trochę lepsze artykułów z odpowiednią ortografią. Jeżeli w moich artykułów znajdują się błędy, daj mnie znać. Pozdrawiam,
::[[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:45, 9 gro 2026 (CET)
== Odpowiedź ==
Cześć, po pierwsze, wybacz, że nie zareagowałem na [[Diskùsëjô brëkòwnika:DawnyTest#Szablónë pòtrzébùją zmianë ë aktualizacëji|twoją starą wiadomość]] o dyskusji na naszej wiki. W pełni się zgadzam, iż zmienione muszą zostać szablony, artykuły itp. Próbowałem podjąć się [[Szablóna:Infobox pòlitik|trudu ich tworzenia]] i edytowania (konkretnie infoboksów). Takowym był ''[[Szablóna:Infobox-państwò]]'', który w mojej opinii posiadał pewne problemy, które udało mi się zaleczyć. Posiłkowałem się m.in. polską wiki i tak na przykład mamy tam rewolucyjny wynalazek opcjonalnych parametrów. Po drugie, odnosząc się do twojej wiadomości, czy jest coś konkretnego co, twoim zdaniem, dobrze by było zmienić w szablonach lub ewentualnie by jakieś szablony utworzyć? Jestem chętny by podjąć się współpracy. Pozdrawiam cieplutko.<br>
<small>A po polsku odpisałem, bo mi było szybciej, nie znam kaszubskiego na tyle, by bezbłędnie wytworzyć taką wiadomość w tak krótkim czasie.</small> [[Brëkòwnik:DawnyTest|DawnyTest]] ([[Diskùsëjô brëkòwnika:DawnyTest|diskùsëjô]]) 21:25, 18 str 2026 (CET)
== Pòdzãkòwanié a pëtanié ==
Laudetur Iesus Christus et Semper Virgo Maria!
Jô bëm chcôł Cë baro pòdzãkòwac za docenienié mòji robòtë z ùsôdzaniém wiadłokôstków dlô kaszëbsczi Wikipedëje a przëznanié mie pòczestny gwiôzdë. Hewò je dlô mie wiôlgą bùchą, eż jô mògã na taczé mòdło czënic nen môl jesz lepszim a snôższim. Takô deja je mie blizą òb robienié nowich szablonów a robòtã nad artiklama, co do jaczich wiedno jô móm starã, abë bëłë jak nôkwalitetniészé ë jak nôbarżi rozkòscérzóné.
Przi ti lëżnoscë jô bëm chcôł Cë ò jedno zapëtôc. Jô trafił niedôwno na twòją starnã brëkòwnikã ë jô móm wiôldżé ùwôżanié do tewò, jak Të fejn to zrobił. Mògã jô ùżëc twòjéwò kòdù do swòjich starnów brëkòwnikã. Jô w nôblëższim czasu chcã zabrac sã za ji ùsôdzenié na pòlsczi a anielsczi Wikipedëjë, a téż jô nie òdsôdzôm ùprawieniégò ë przebùdérowaniéwò téż mòji starnë brëkòwnika z kaszëbsczi Wikipedëjë. Jô z górë dzãkùjã, pòzdrówiôm a żëczã bëlni, ùbëtni nocë. Òstani z Bògã!
PS. Jakbyś czegoś nie rozumiał z tego, co wyżej napisałem, śmiało napisz, a ja to ci to napiszę po polsku. [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 01:29, 20 łżë 2026 (CEST)
:Mòjn ë ni mô sprawë, panie Jakùbie! Przédnã przëczënã, dlôcze przëznôł pòczestny gwiôzdë za stwòrzenié zacht szablónów i zbògacënié kaszëbsczi Wikipedie je, że w kaszëbsczi Wikipedie, tak jak wszelejaczé wersje môlowëch jãzëków nicht nie przëznôlë zwëkòwich abò wiãkszëch brëkòwników nôdgrodë, żebë mòtiwòwac i òstac na długò, a pózni przestôlë prôcowac dlô kaszëbsczi Wikipedie i òpùscëc to całowno. Dlôtegò pòstanowił przëznac dlô Cebie gwiôzdë, żëbë mòtiwòwac i òstac na długò. Òczëwisce ni mògã dużo gadac z tim, co napisôł. Bóg zapłac za stwòrzenié dlô mie diskùsje, bò z tim mie ceszy :) I żëczë bëlno zdrowiégò i ùcemiãgã za stwòrzenié taczé fejn cuda. (wëbaczë za pisënkòwé felë) Gòrąco pòzdrôwióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:05, 20 łżë 2026 (CEST)
::Jô téż pòzdrówiôm. Le co do mòjégò pëtaniégò niżi, wëdôlmaczã sprawã pò pòlskù, abë bëło lżi to bëlno zrozmiec.
::Zauważyłem, że fajnie zbudowałeś swoją stronę użytkownika na kaszubskiej Wikipedii. Bardzo mi podoba rozwiązanie, jakiego użyłeś, by to usystematyzować. Pytałem zaś o to, czy mógłbym w przyszłości użyć twojego kodu przy tworzeniu moich osobistych stron użytkownika? Myślę tu głównie o stronie na polską Wikipedię (cóż, jako że tam również coś czasem napiszę lub zedytuję, wypadałoby, abym w końcu ją stworzył XD),oraz anglojęzyczną, ale nie wykluczam też w przyszłości przebudowy tej już stworzonej przeze mnie na kaszubskiej Wikipedii. [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 13:05, 21 łżë 2026 (CEST)
== Pòdzãkòwanié za przeniosbã ==
Wiôldżé Bóg zapłac Cë za przeniesenié artikla ò Mòdżorów Pioterze na jegò docélowi môl. Jô jem baro rôd, eż mòja robòta je òbzéranô a docenianiô bez jinszich wikipedistów. Z drëdżi starnë jô baro przeprôszóm za felënk zdrzódłów. To, eż jô jesz jich ni miôł dodóné, bëło jedurnym pòwòdã, dlô jaczégò articzel béł jesz w zapisownikù. Wczorô miôł jem to wnetka zrobióné, le pò czilkù gòdzënach zmërkôł jem, eż jô nie zapisôł wińdzeniégò swòji robòtë, a jakno eż mòji laptop sã aùtomaticzno zrestartowôł, stracëł jem wszëtczé brzôdë mòji robòtë z ùzdrzódłowianiém. Terôzka jô mdã te zdrzódła dodawac òd zôczątkù a jô mdã miec starã, abë jesz dzys ònë bëłë dodóné. Jesz rôz dzãkã a żëczã jem milëczni majówczi. :) [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 12:17, 1 môj 2026 (CEST)
:Mòjn ë ni mô sprawë! Przédnã przëczënã, dlôcze przeniosł twój zapisownik-articzel ò Mòdżorów Pioterze na jegò docélowi môl, to że twój zapisownik wëzdrzi jak na dłudżi i òsoblëwi, pòniewôż w kaszëbsczi Wikipedie mómë za mało dłudżich articzlów, bò ni chcą pòswiãcëwac przënômni 30% czasu na editowanié i dofùlowiwanié kaszëbsczich rzeczy, dopòczi staną sã niaktualné i stôré. Dlôte pòstanowił to robic. Òczëwisce w dodôtkù dodôł wiki-fónkcje taczich jak Commons, kategòrie i zacht jinszëch i kòrektowôł je. Nic sã ni stało, że ni dodôł u Cebie zdrzódłów, bò nierôz mògą bëc problemë. Òczëwisce czejbë chcesz zdolmaczëc interfejs MediaWiki na kaszëbsczi (bò stwòrzëł w Karczmie diskùsje), klëkni tuwò: [https://translatewiki.net TranslateWiki]! Dzãka za stwòrzenié diskùsje ò mie :) Cebie téż żëczã jem milëczni majówczi i Òstani z Bògã! Pòzdrówióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:59, 1 môj 2026 (CEST)
== Słów czile ==
Mòjn! Miło je widzec, że persónë taczé jak të, chcą sã angażowac i robic dlô kaszëbiznë na tak apartny métel jak rozwij Wikipedie – móm dlô ce bédënk, cobë më z @[[Brëkòwnik:Rzadtymczasowy|Rzadtymczasowy]] mòglë bëc z Tobą w łączbie, pòmagac (np. jinyma słowarzama niż niedoskònałi sloworz.org) i dzejac mët, môsz të Discorda abò cos jinszégò do kòmùnikacje? [[Brëkòwnik:Terrus38|Terrus38]] ([[Diskùsëjô brëkòwnika:Terrus38|diskùsëjô]]) 15:35, 15 cze 2026 (CEST)
:Witam, panie Terrus! Na razie nie będę mówił po kaszubsku, bo słowniki kaszubskie w internecie są jeszcze ograniczone, ale na końcu wstawię pozdrowienie i podziękowanie do Ciebie. Dziękuję za poprawki gramatyczne moich tłumaczeń w TranslateWiki. I oczywiście odkąd zrobiłeś dzisiaj dyskusję, tak, ja zaangażowałem do dyskusji dotyczące gramatyki kaszubszczyzny, bo pan i twoi koledzy robicie małą debatę. Mówiąc zaangażowaniu, będę podawał kroki:
:# Nie posiadam żadnych komunikatorów, bo komunikatory według mnie są nieprzydatne i niepotrzebne, e-mail też.
:# Na razie wolę robić edycje w kaszubskiej Wikipedii i je rozbudować.
:PS: Napisałem odrobinę profesjonalnie, bo tak muszę. Dzãkùjã baro za stwòrzenié diskùsje dlô mie i serdeczno pòzdrówióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 16:22, 15 cze 2026 (CEST)
</div>
== Òddzãkòwanié ==
Adjé, Orbitminis, dzãka za Twój wkłôd we Wikipediã i żëczã wszëtczégò bëlnégò! Jô ale prosto pòprôwiôł pisënk a sztilistikã pò pòprzédnëch edicjach, jô nie rëmôł bezpòtrzébno akapitów encyklopedicznégò tekstu, jaczi pisalë jiny, czë Të, czë jinszi. Jô nie jem Tobie za wroga, a za wespółrobòtnika. W kòżdim razu, pòzdrówk. [[Brëkòwnik:Terrus38|Terrus38]] ([[Diskùsëjô brëkòwnika:Terrus38|diskùsëjô]]) 14:50, 5 lëp 2026 (CEST)
47w3gceeaeidl106h5cu3ha6u9rgrbh
204445
204442
2026-07-05T13:51:18Z
Orbitminis
18250
/* Òddzãkòwanié */ Odpowiedź
204445
wikitext
text/x-wiki
<div align="center" class="usermessage">Witôjtaż w mòji diskùsji brëkòwnika! Mòżesz téż pisac pò kaszëbskù, pò pòlskù, pò anielskù i pò miemiecckù, a pònemù bãdã òdpòwiadał.
----[[Òbrôzk:Sandbox.svg|lewo|bezramki|78x78px]][[Brëkòwnik:Orbitminis/Zapisownik|<big>ZAPISOWNIK</big>]] [[Brëkòwnik:Orbitminis|<big>Ò MIE</big>]] [[Òbrôzk:Debate,_discussion.jpg|bezramki|195x195px]] '''<big>DISKÙSËJÔ BRËKÒWNIKA</big>'''
</div>
<div align="center" class="disscusion">
== Poprawanie linków ==
Hej. Nie musisz wykonywać takich poprawek, jak <tt><nowiki>[[Gduńsk]]ù -> [[Gduńsk|Gduńskù]]</nowiki></tt> – w najbliższych dniach (może tygodniach) konfiguracja linków zostanie naprawiona na poziomie całej wiki. Pozdrawiam, [[Brëkòwnik:Tsca|tsca]] ([[Diskùsëjô brëkòwnika:Tsca|diskùsëjô]]) 21:01, 31 zél 2025 (CEST)
:Wiem, proszę pana. Ale ja muszę poprawiać kaszubską Wikipedię, żeby nie były takich błędów ortograficznych jak "[[Gduńsk]]ù" i "Jop Polsczei jaczieego", bo te błędy mogą sprawiać kaszubską Wikipedię jako encyklopedię trzeciego świata jak najmniejsze Wikipedie w różnych językach. I oczywiście te źródła, artykuły, samouczki i szablony niektóre wyglądają staro jak w latach 2000-2010, więc muszą mieć nowe źródła, świeże infomacje i szablony podobne do polskiej Wikipedii po kaszubsku. Więc przepraszam najmocniej za takie, które spełniłem. Pozdrawiam, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 10:12, 1 Séw 2025 (CEST)
== Automatycznie tłumaczone artykuły ==
Wklejanie automatycznie tłumaczonych artykułów mija się z celem. Skoro, jak sam deklarujesz na swojej stronie użytkownika, nie znasz języka kaszubskiego, nie jesteś w stanie zauważyć ani poprawić błędów w maszynowo przetłumaczonych treściach. Hasła utworzone w ten sposób niestety muszą zostać usunięte. [[Brëkòwnik:Tsca|tsca]] ([[Diskùsëjô brëkòwnika:Tsca|diskùsëjô]]) 23:28, 29 ruj 2025 (CET)
:czemu? [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 16:11, 18 lës 2025 (CET)
::CSB: Mòjn panie Tsca! Dzysë jô sã dowiedzôł twòją nôùczkã, żëbë nie wlëmic "aùtomaticznie dolmaczonëch artiklów". Wiãc pòstanowił ùżëc słowôrza do stwórzbë artiklów, żëbë bëło trochã lepszé artiklów z pasëjącą pisënką. Żle w mojich artiklach nalézają sã felë, dôj mni znac. Pozdrôwióm, Orbitminis.
::PL: Cześć, panie Tsca! Dzisiaj ja się dowiedziałem twoją nauczkę, żeby nie wklejać „automatycznie tłumaczonych artykułów”. Więc postanowiłem użyć słownika do stworzenia artykułów, żeby było trochę lepsze artykułów z odpowiednią ortografią. Jeżeli w moich artykułów znajdują się błędy, daj mnie znać. Pozdrawiam,
::[[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:45, 9 gro 2026 (CET)
== Odpowiedź ==
Cześć, po pierwsze, wybacz, że nie zareagowałem na [[Diskùsëjô brëkòwnika:DawnyTest#Szablónë pòtrzébùją zmianë ë aktualizacëji|twoją starą wiadomość]] o dyskusji na naszej wiki. W pełni się zgadzam, iż zmienione muszą zostać szablony, artykuły itp. Próbowałem podjąć się [[Szablóna:Infobox pòlitik|trudu ich tworzenia]] i edytowania (konkretnie infoboksów). Takowym był ''[[Szablóna:Infobox-państwò]]'', który w mojej opinii posiadał pewne problemy, które udało mi się zaleczyć. Posiłkowałem się m.in. polską wiki i tak na przykład mamy tam rewolucyjny wynalazek opcjonalnych parametrów. Po drugie, odnosząc się do twojej wiadomości, czy jest coś konkretnego co, twoim zdaniem, dobrze by było zmienić w szablonach lub ewentualnie by jakieś szablony utworzyć? Jestem chętny by podjąć się współpracy. Pozdrawiam cieplutko.<br>
<small>A po polsku odpisałem, bo mi było szybciej, nie znam kaszubskiego na tyle, by bezbłędnie wytworzyć taką wiadomość w tak krótkim czasie.</small> [[Brëkòwnik:DawnyTest|DawnyTest]] ([[Diskùsëjô brëkòwnika:DawnyTest|diskùsëjô]]) 21:25, 18 str 2026 (CET)
== Pòdzãkòwanié a pëtanié ==
Laudetur Iesus Christus et Semper Virgo Maria!
Jô bëm chcôł Cë baro pòdzãkòwac za docenienié mòji robòtë z ùsôdzaniém wiadłokôstków dlô kaszëbsczi Wikipedëje a przëznanié mie pòczestny gwiôzdë. Hewò je dlô mie wiôlgą bùchą, eż jô mògã na taczé mòdło czënic nen môl jesz lepszim a snôższim. Takô deja je mie blizą òb robienié nowich szablonów a robòtã nad artiklama, co do jaczich wiedno jô móm starã, abë bëłë jak nôkwalitetniészé ë jak nôbarżi rozkòscérzóné.
Przi ti lëżnoscë jô bëm chcôł Cë ò jedno zapëtôc. Jô trafił niedôwno na twòją starnã brëkòwnikã ë jô móm wiôldżé ùwôżanié do tewò, jak Të fejn to zrobił. Mògã jô ùżëc twòjéwò kòdù do swòjich starnów brëkòwnikã. Jô w nôblëższim czasu chcã zabrac sã za ji ùsôdzenié na pòlsczi a anielsczi Wikipedëjë, a téż jô nie òdsôdzôm ùprawieniégò ë przebùdérowaniéwò téż mòji starnë brëkòwnika z kaszëbsczi Wikipedëjë. Jô z górë dzãkùjã, pòzdrówiôm a żëczã bëlni, ùbëtni nocë. Òstani z Bògã!
PS. Jakbyś czegoś nie rozumiał z tego, co wyżej napisałem, śmiało napisz, a ja to ci to napiszę po polsku. [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 01:29, 20 łżë 2026 (CEST)
:Mòjn ë ni mô sprawë, panie Jakùbie! Przédnã przëczënã, dlôcze przëznôł pòczestny gwiôzdë za stwòrzenié zacht szablónów i zbògacënié kaszëbsczi Wikipedie je, że w kaszëbsczi Wikipedie, tak jak wszelejaczé wersje môlowëch jãzëków nicht nie przëznôlë zwëkòwich abò wiãkszëch brëkòwników nôdgrodë, żebë mòtiwòwac i òstac na długò, a pózni przestôlë prôcowac dlô kaszëbsczi Wikipedie i òpùscëc to całowno. Dlôtegò pòstanowił przëznac dlô Cebie gwiôzdë, żëbë mòtiwòwac i òstac na długò. Òczëwisce ni mògã dużo gadac z tim, co napisôł. Bóg zapłac za stwòrzenié dlô mie diskùsje, bò z tim mie ceszy :) I żëczë bëlno zdrowiégò i ùcemiãgã za stwòrzenié taczé fejn cuda. (wëbaczë za pisënkòwé felë) Gòrąco pòzdrôwióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:05, 20 łżë 2026 (CEST)
::Jô téż pòzdrówiôm. Le co do mòjégò pëtaniégò niżi, wëdôlmaczã sprawã pò pòlskù, abë bëło lżi to bëlno zrozmiec.
::Zauważyłem, że fajnie zbudowałeś swoją stronę użytkownika na kaszubskiej Wikipedii. Bardzo mi podoba rozwiązanie, jakiego użyłeś, by to usystematyzować. Pytałem zaś o to, czy mógłbym w przyszłości użyć twojego kodu przy tworzeniu moich osobistych stron użytkownika? Myślę tu głównie o stronie na polską Wikipedię (cóż, jako że tam również coś czasem napiszę lub zedytuję, wypadałoby, abym w końcu ją stworzył XD),oraz anglojęzyczną, ale nie wykluczam też w przyszłości przebudowy tej już stworzonej przeze mnie na kaszubskiej Wikipedii. [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 13:05, 21 łżë 2026 (CEST)
== Pòdzãkòwanié za przeniosbã ==
Wiôldżé Bóg zapłac Cë za przeniesenié artikla ò Mòdżorów Pioterze na jegò docélowi môl. Jô jem baro rôd, eż mòja robòta je òbzéranô a docenianiô bez jinszich wikipedistów. Z drëdżi starnë jô baro przeprôszóm za felënk zdrzódłów. To, eż jô jesz jich ni miôł dodóné, bëło jedurnym pòwòdã, dlô jaczégò articzel béł jesz w zapisownikù. Wczorô miôł jem to wnetka zrobióné, le pò czilkù gòdzënach zmërkôł jem, eż jô nie zapisôł wińdzeniégò swòji robòtë, a jakno eż mòji laptop sã aùtomaticzno zrestartowôł, stracëł jem wszëtczé brzôdë mòji robòtë z ùzdrzódłowianiém. Terôzka jô mdã te zdrzódła dodawac òd zôczątkù a jô mdã miec starã, abë jesz dzys ònë bëłë dodóné. Jesz rôz dzãkã a żëczã jem milëczni majówczi. :) [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 12:17, 1 môj 2026 (CEST)
:Mòjn ë ni mô sprawë! Przédnã przëczënã, dlôcze przeniosł twój zapisownik-articzel ò Mòdżorów Pioterze na jegò docélowi môl, to że twój zapisownik wëzdrzi jak na dłudżi i òsoblëwi, pòniewôż w kaszëbsczi Wikipedie mómë za mało dłudżich articzlów, bò ni chcą pòswiãcëwac przënômni 30% czasu na editowanié i dofùlowiwanié kaszëbsczich rzeczy, dopòczi staną sã niaktualné i stôré. Dlôte pòstanowił to robic. Òczëwisce w dodôtkù dodôł wiki-fónkcje taczich jak Commons, kategòrie i zacht jinszëch i kòrektowôł je. Nic sã ni stało, że ni dodôł u Cebie zdrzódłów, bò nierôz mògą bëc problemë. Òczëwisce czejbë chcesz zdolmaczëc interfejs MediaWiki na kaszëbsczi (bò stwòrzëł w Karczmie diskùsje), klëkni tuwò: [https://translatewiki.net TranslateWiki]! Dzãka za stwòrzenié diskùsje ò mie :) Cebie téż żëczã jem milëczni majówczi i Òstani z Bògã! Pòzdrówióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:59, 1 môj 2026 (CEST)
== Słów czile ==
Mòjn! Miło je widzec, że persónë taczé jak të, chcą sã angażowac i robic dlô kaszëbiznë na tak apartny métel jak rozwij Wikipedie – móm dlô ce bédënk, cobë më z @[[Brëkòwnik:Rzadtymczasowy|Rzadtymczasowy]] mòglë bëc z Tobą w łączbie, pòmagac (np. jinyma słowarzama niż niedoskònałi sloworz.org) i dzejac mët, môsz të Discorda abò cos jinszégò do kòmùnikacje? [[Brëkòwnik:Terrus38|Terrus38]] ([[Diskùsëjô brëkòwnika:Terrus38|diskùsëjô]]) 15:35, 15 cze 2026 (CEST)
:Witam, panie Terrus! Na razie nie będę mówił po kaszubsku, bo słowniki kaszubskie w internecie są jeszcze ograniczone, ale na końcu wstawię pozdrowienie i podziękowanie do Ciebie. Dziękuję za poprawki gramatyczne moich tłumaczeń w TranslateWiki. I oczywiście odkąd zrobiłeś dzisiaj dyskusję, tak, ja zaangażowałem do dyskusji dotyczące gramatyki kaszubszczyzny, bo pan i twoi koledzy robicie małą debatę. Mówiąc zaangażowaniu, będę podawał kroki:
:# Nie posiadam żadnych komunikatorów, bo komunikatory według mnie są nieprzydatne i niepotrzebne, e-mail też.
:# Na razie wolę robić edycje w kaszubskiej Wikipedii i je rozbudować.
:PS: Napisałem odrobinę profesjonalnie, bo tak muszę. Dzãkùjã baro za stwòrzenié diskùsje dlô mie i serdeczno pòzdrówióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 16:22, 15 cze 2026 (CEST)
</div>
== Òddzãkòwanié ==
Adjé, Orbitminis, dzãka za Twój wkłôd we Wikipediã i żëczã wszëtczégò bëlnégò! Jô ale prosto pòprôwiôł pisënk a sztilistikã pò pòprzédnëch edicjach, jô nie rëmôł bezpòtrzébno akapitów encyklopedicznégò tekstu, jaczi pisalë jiny, czë Të, czë jinszi. Jô nie jem Tobie za wroga, a za wespółrobòtnika. W kòżdim razu, pòzdrówk. [[Brëkòwnik:Terrus38|Terrus38]] ([[Diskùsëjô brëkòwnika:Terrus38|diskùsëjô]]) 14:50, 5 lëp 2026 (CEST)
:Witôjtaż baro serdeczno, panie Terrusie!
:Ostatnio zakończyłem/zawiesiłem moją działalność na zawsze ze względu, że nie chciałem przez całe lato i życie edytować cały czas na kaszubskiej Wikipedii z powodu znajomości i wolnego stylu edytowania korzystając ze słowników takich jak wspomniany przez Ciebie wcześniej „niedokładnego [[Internetowi Słowôrz Kaszëbsczégò Jãzëka|Internetowégò Słowarza Kaszëbsczégò Jãzëka]]” i Glosbe (przejście polsko-kaszubskie). Ja chciałem tylko zbogacić, a unowocześnić waszą encyklopedię polegając informacji z polskiej Wikipedii jako hobby albo cel do uratowania mniejszościowych języków Polski (ostatnio rozszerzyłem informacje o religijnych postaciach [[Mirosłôw Adamczik|Mirosława Adamczyka]] i [[Wòjcech Cëchòsz|Wòjcecha Cëchòsza]]) i podczas wybuchu ilości moich wkładów dodałem sporo własnych i samodzielnych informacji w artykule [[Méster Bëlnégò Czëtaniô (kònkùrs)|Méster Bëlnégò Czëtaniô]] z przepisami. I nie tylko poprawiłeś niektóre moje edycje, ale także użyłeś mojego userboksa (potocznie kaszubskie: ''brëkòkôstka'') dotyczące o małą ilością artykułów w waszej encyklopedii przed usunięciem mojej strony, ale mimo wszystkiego, czasami albo rzadko robię problemy z przeniesieniami i ortografią ze względu trudności, leksyki i mniejszą ilością zasobów kaszubskiego języka w Internecie (mimo że np. w Youtubie jest ich dużo filmików i sporo kanałów po kaszubsku).
:Dziękuję panu za twoje pańskie miłe słowa, stworzenie dyskusji i uznawanie mnie za współpracownika, bo spędziłem dokładnie rok i pół czasu, żeby zbudować i poświęcić na tworzenie moich tłumaczeń kaszubskich do artykułów i szablonów! (przełączę na kaszubski) Cebie téż żëczã wszëtczégò bëlnégò i dalszégò rozwiju waji encyklopedie i mileczni, robòcy ferie, bò jes cëdownym editorã, chtëren chcôł zlepszëc i rëmąc wszelczé problemë nie leno techniczno, le téż òrtograficzno waji encyklopedie, żebë Kaszëbskô Wikipediô stała sã wikszim i encyklopedicznym placã pò kaszëbskù.
:PS. Przepraszam za błędy ortograficzne w języku kaszubskim, bo korzystałem słowniki.
:Gòrący pòzdrówk, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 15:51, 5 lëp 2026 (CEST)
qqdlus2rb738wu1cum5380dy2d5gte1
204446
204445
2026-07-05T13:53:44Z
Orbitminis
18250
drob.
204446
wikitext
text/x-wiki
<div align="center" class="usermessage">Witôjtaż w mòji diskùsji brëkòwnika! Mòżesz téż pisac pò kaszëbskù, pò pòlskù, pò anielskù i pò miemiecckù, a pònemù bãdã òdpòwiadał.
----[[Òbrôzk:Sandbox.svg|lewo|bezramki|78x78px]][[Brëkòwnik:Orbitminis/Zapisownik|<big>ZAPISOWNIK</big>]] [[Brëkòwnik:Orbitminis|<big>Ò MIE</big>]] [[Òbrôzk:Debate,_discussion.jpg|bezramki|195x195px]] '''<big>DISKÙSËJÔ BRËKÒWNIKA</big>'''
</div>
<div align="center" class="disscusion">
== Poprawanie linków ==
Hej. Nie musisz wykonywać takich poprawek, jak <tt><nowiki>[[Gduńsk]]ù -> [[Gduńsk|Gduńskù]]</nowiki></tt> – w najbliższych dniach (może tygodniach) konfiguracja linków zostanie naprawiona na poziomie całej wiki. Pozdrawiam, [[Brëkòwnik:Tsca|tsca]] ([[Diskùsëjô brëkòwnika:Tsca|diskùsëjô]]) 21:01, 31 zél 2025 (CEST)
:Wiem, proszę pana. Ale ja muszę poprawiać kaszubską Wikipedię, żeby nie były takich błędów ortograficznych jak "[[Gduńsk]]ù" i "Jop Polsczei jaczieego", bo te błędy mogą sprawiać kaszubską Wikipedię jako encyklopedię trzeciego świata jak najmniejsze Wikipedie w różnych językach. I oczywiście te źródła, artykuły, samouczki i szablony niektóre wyglądają staro jak w latach 2000-2010, więc muszą mieć nowe źródła, świeże infomacje i szablony podobne do polskiej Wikipedii po kaszubsku. Więc przepraszam najmocniej za takie, które spełniłem. Pozdrawiam, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 10:12, 1 Séw 2025 (CEST)
== Automatycznie tłumaczone artykuły ==
Wklejanie automatycznie tłumaczonych artykułów mija się z celem. Skoro, jak sam deklarujesz na swojej stronie użytkownika, nie znasz języka kaszubskiego, nie jesteś w stanie zauważyć ani poprawić błędów w maszynowo przetłumaczonych treściach. Hasła utworzone w ten sposób niestety muszą zostać usunięte. [[Brëkòwnik:Tsca|tsca]] ([[Diskùsëjô brëkòwnika:Tsca|diskùsëjô]]) 23:28, 29 ruj 2025 (CET)
:czemu? [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 16:11, 18 lës 2025 (CET)
::CSB: Mòjn panie Tsca! Dzysë jô sã dowiedzôł twòją nôùczkã, żëbë nie wlëmic "aùtomaticznie dolmaczonëch artiklów". Wiãc pòstanowił ùżëc słowôrza do stwórzbë artiklów, żëbë bëło trochã lepszé artiklów z pasëjącą pisënką. Żle w mojich artiklach nalézają sã felë, dôj mni znac. Pozdrôwióm, Orbitminis.
::PL: Cześć, panie Tsca! Dzisiaj ja się dowiedziałem twoją nauczkę, żeby nie wklejać „automatycznie tłumaczonych artykułów”. Więc postanowiłem użyć słownika do stworzenia artykułów, żeby było trochę lepsze artykułów z odpowiednią ortografią. Jeżeli w moich artykułów znajdują się błędy, daj mnie znać. Pozdrawiam,
::[[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:45, 9 gro 2026 (CET)
== Odpowiedź ==
Cześć, po pierwsze, wybacz, że nie zareagowałem na [[Diskùsëjô brëkòwnika:DawnyTest#Szablónë pòtrzébùją zmianë ë aktualizacëji|twoją starą wiadomość]] o dyskusji na naszej wiki. W pełni się zgadzam, iż zmienione muszą zostać szablony, artykuły itp. Próbowałem podjąć się [[Szablóna:Infobox pòlitik|trudu ich tworzenia]] i edytowania (konkretnie infoboksów). Takowym był ''[[Szablóna:Infobox-państwò]]'', który w mojej opinii posiadał pewne problemy, które udało mi się zaleczyć. Posiłkowałem się m.in. polską wiki i tak na przykład mamy tam rewolucyjny wynalazek opcjonalnych parametrów. Po drugie, odnosząc się do twojej wiadomości, czy jest coś konkretnego co, twoim zdaniem, dobrze by było zmienić w szablonach lub ewentualnie by jakieś szablony utworzyć? Jestem chętny by podjąć się współpracy. Pozdrawiam cieplutko.<br>
<small>A po polsku odpisałem, bo mi było szybciej, nie znam kaszubskiego na tyle, by bezbłędnie wytworzyć taką wiadomość w tak krótkim czasie.</small> [[Brëkòwnik:DawnyTest|DawnyTest]] ([[Diskùsëjô brëkòwnika:DawnyTest|diskùsëjô]]) 21:25, 18 str 2026 (CET)
== Pòdzãkòwanié a pëtanié ==
Laudetur Iesus Christus et Semper Virgo Maria!
Jô bëm chcôł Cë baro pòdzãkòwac za docenienié mòji robòtë z ùsôdzaniém wiadłokôstków dlô kaszëbsczi Wikipedëje a przëznanié mie pòczestny gwiôzdë. Hewò je dlô mie wiôlgą bùchą, eż jô mògã na taczé mòdło czënic nen môl jesz lepszim a snôższim. Takô deja je mie blizą òb robienié nowich szablonów a robòtã nad artiklama, co do jaczich wiedno jô móm starã, abë bëłë jak nôkwalitetniészé ë jak nôbarżi rozkòscérzóné.
Przi ti lëżnoscë jô bëm chcôł Cë ò jedno zapëtôc. Jô trafił niedôwno na twòją starnã brëkòwnikã ë jô móm wiôldżé ùwôżanié do tewò, jak Të fejn to zrobił. Mògã jô ùżëc twòjéwò kòdù do swòjich starnów brëkòwnikã. Jô w nôblëższim czasu chcã zabrac sã za ji ùsôdzenié na pòlsczi a anielsczi Wikipedëjë, a téż jô nie òdsôdzôm ùprawieniégò ë przebùdérowaniéwò téż mòji starnë brëkòwnika z kaszëbsczi Wikipedëjë. Jô z górë dzãkùjã, pòzdrówiôm a żëczã bëlni, ùbëtni nocë. Òstani z Bògã!
PS. Jakbyś czegoś nie rozumiał z tego, co wyżej napisałem, śmiało napisz, a ja to ci to napiszę po polsku. [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 01:29, 20 łżë 2026 (CEST)
:Mòjn ë ni mô sprawë, panie Jakùbie! Przédnã przëczënã, dlôcze przëznôł pòczestny gwiôzdë za stwòrzenié zacht szablónów i zbògacënié kaszëbsczi Wikipedie je, że w kaszëbsczi Wikipedie, tak jak wszelejaczé wersje môlowëch jãzëków nicht nie przëznôlë zwëkòwich abò wiãkszëch brëkòwników nôdgrodë, żebë mòtiwòwac i òstac na długò, a pózni przestôlë prôcowac dlô kaszëbsczi Wikipedie i òpùscëc to całowno. Dlôtegò pòstanowił przëznac dlô Cebie gwiôzdë, żëbë mòtiwòwac i òstac na długò. Òczëwisce ni mògã dużo gadac z tim, co napisôł. Bóg zapłac za stwòrzenié dlô mie diskùsje, bò z tim mie ceszy :) I żëczë bëlno zdrowiégò i ùcemiãgã za stwòrzenié taczé fejn cuda. (wëbaczë za pisënkòwé felë) Gòrąco pòzdrôwióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:05, 20 łżë 2026 (CEST)
::Jô téż pòzdrówiôm. Le co do mòjégò pëtaniégò niżi, wëdôlmaczã sprawã pò pòlskù, abë bëło lżi to bëlno zrozmiec.
::Zauważyłem, że fajnie zbudowałeś swoją stronę użytkownika na kaszubskiej Wikipedii. Bardzo mi podoba rozwiązanie, jakiego użyłeś, by to usystematyzować. Pytałem zaś o to, czy mógłbym w przyszłości użyć twojego kodu przy tworzeniu moich osobistych stron użytkownika? Myślę tu głównie o stronie na polską Wikipedię (cóż, jako że tam również coś czasem napiszę lub zedytuję, wypadałoby, abym w końcu ją stworzył XD),oraz anglojęzyczną, ale nie wykluczam też w przyszłości przebudowy tej już stworzonej przeze mnie na kaszubskiej Wikipedii. [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 13:05, 21 łżë 2026 (CEST)
== Pòdzãkòwanié za przeniosbã ==
Wiôldżé Bóg zapłac Cë za przeniesenié artikla ò Mòdżorów Pioterze na jegò docélowi môl. Jô jem baro rôd, eż mòja robòta je òbzéranô a docenianiô bez jinszich wikipedistów. Z drëdżi starnë jô baro przeprôszóm za felënk zdrzódłów. To, eż jô jesz jich ni miôł dodóné, bëło jedurnym pòwòdã, dlô jaczégò articzel béł jesz w zapisownikù. Wczorô miôł jem to wnetka zrobióné, le pò czilkù gòdzënach zmërkôł jem, eż jô nie zapisôł wińdzeniégò swòji robòtë, a jakno eż mòji laptop sã aùtomaticzno zrestartowôł, stracëł jem wszëtczé brzôdë mòji robòtë z ùzdrzódłowianiém. Terôzka jô mdã te zdrzódła dodawac òd zôczątkù a jô mdã miec starã, abë jesz dzys ònë bëłë dodóné. Jesz rôz dzãkã a żëczã jem milëczni majówczi. :) [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 12:17, 1 môj 2026 (CEST)
:Mòjn ë ni mô sprawë! Przédnã przëczënã, dlôcze przeniosł twój zapisownik-articzel ò Mòdżorów Pioterze na jegò docélowi môl, to że twój zapisownik wëzdrzi jak na dłudżi i òsoblëwi, pòniewôż w kaszëbsczi Wikipedie mómë za mało dłudżich articzlów, bò ni chcą pòswiãcëwac przënômni 30% czasu na editowanié i dofùlowiwanié kaszëbsczich rzeczy, dopòczi staną sã niaktualné i stôré. Dlôte pòstanowił to robic. Òczëwisce w dodôtkù dodôł wiki-fónkcje taczich jak Commons, kategòrie i zacht jinszëch i kòrektowôł je. Nic sã ni stało, że ni dodôł u Cebie zdrzódłów, bò nierôz mògą bëc problemë. Òczëwisce czejbë chcesz zdolmaczëc interfejs MediaWiki na kaszëbsczi (bò stwòrzëł w Karczmie diskùsje), klëkni tuwò: [https://translatewiki.net TranslateWiki]! Dzãka za stwòrzenié diskùsje ò mie :) Cebie téż żëczã jem milëczni majówczi i Òstani z Bògã! Pòzdrówióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:59, 1 môj 2026 (CEST)
== Słów czile ==
Mòjn! Miło je widzec, że persónë taczé jak të, chcą sã angażowac i robic dlô kaszëbiznë na tak apartny métel jak rozwij Wikipedie – móm dlô ce bédënk, cobë më z @[[Brëkòwnik:Rzadtymczasowy|Rzadtymczasowy]] mòglë bëc z Tobą w łączbie, pòmagac (np. jinyma słowarzama niż niedoskònałi sloworz.org) i dzejac mët, môsz të Discorda abò cos jinszégò do kòmùnikacje? [[Brëkòwnik:Terrus38|Terrus38]] ([[Diskùsëjô brëkòwnika:Terrus38|diskùsëjô]]) 15:35, 15 cze 2026 (CEST)
:Witam, panie Terrus! Na razie nie będę mówił po kaszubsku, bo słowniki kaszubskie w internecie są jeszcze ograniczone, ale na końcu wstawię pozdrowienie i podziękowanie do Ciebie. Dziękuję za poprawki gramatyczne moich tłumaczeń w TranslateWiki. I oczywiście odkąd zrobiłeś dzisiaj dyskusję, tak, ja zaangażowałem do dyskusji dotyczące gramatyki kaszubszczyzny, bo pan i twoi koledzy robicie małą debatę. Mówiąc zaangażowaniu, będę podawał kroki:
:# Nie posiadam żadnych komunikatorów, bo komunikatory według mnie są nieprzydatne i niepotrzebne, e-mail też.
:# Na razie wolę robić edycje w kaszubskiej Wikipedii i je rozbudować.
:PS: Napisałem odrobinę profesjonalnie, bo tak muszę. Dzãkùjã baro za stwòrzenié diskùsje dlô mie i serdeczno pòzdrówióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 16:22, 15 cze 2026 (CEST)
</div>
== Òddzãkòwanié ==
Adjé, Orbitminis, dzãka za Twój wkłôd we Wikipediã i żëczã wszëtczégò bëlnégò! Jô ale prosto pòprôwiôł pisënk a sztilistikã pò pòprzédnëch edicjach, jô nie rëmôł bezpòtrzébno akapitów encyklopedicznégò tekstu, jaczi pisalë jiny, czë Të, czë jinszi. Jô nie jem Tobie za wroga, a za wespółrobòtnika. W kòżdim razu, pòzdrówk. [[Brëkòwnik:Terrus38|Terrus38]] ([[Diskùsëjô brëkòwnika:Terrus38|diskùsëjô]]) 14:50, 5 lëp 2026 (CEST)
:Witôjtaż baro serdeczno, panie Terrusie!
:Ostatnio zakończyłem/zawiesiłem moją działalność na zawsze ze względu, że nie chciałem przez całe lato i życie edytować cały czas na kaszubskiej Wikipedii z powodu znajomości i wolnego stylu edytowania korzystając ze słowników takich jak wspomniany przez Ciebie wcześniej „niedokładnego [[Internetowi Słowôrz Kaszëbsczégò Jãzëka|Internetowégò Słowarza Kaszëbsczégò Jãzëka]]” i Glosbe (przejście polsko-kaszubskie). Ja chciałem tylko zbogacić, a unowocześnić waszą encyklopedię polegając informacji z polskiej Wikipedii jako hobby albo cel do uratowania mniejszościowych języków Polski (ostatnio rozszerzyłem informacje o religijnych postaciach [[Mirosłôw Adamczik|Mirosława Adamczyka]] i [[Wòjcech Cëchòsz|Wòjcecha Cëchòsza]]) i podczas wybuchu ilości moich wkładów dodałem sporo własnych i samodzielnych informacji w artykule [[Méster Bëlnégò Czëtaniô (kònkùrs)|Méster Bëlnégò Czëtaniô]] z przepisami. I nie tylko poprawiłeś niektóre moje edycje, ale także użyłeś mojego userboksa (potocznie kaszubskie: ''brëkòkôstka'') dotyczące o małą ilością artykułów w waszej encyklopedii przed usunięciem mojej strony, ale mimo wszystkiego, czasami albo rzadko robię problemy z przeniesieniami i ortografią ze względu trudności, leksyki i mniejszą ilością zasobów kaszubskiego języka w Internecie (mimo że np. w Youtubie jest ich dużo filmików i sporo kanałów po kaszubsku).
:Dziękuję panu za twoje pańskie miłe słowa, stworzenie dyskusji i uznawanie mnie za współpracownika, bo spędziłem dokładnie rok i pół czasu, żeby zbudować i poświęcić na tworzenie moich tłumaczeń kaszubskich do artykułów i szablonów! (przełączę na kaszubski) Cebie téż żëczã wszëtczégò bëlnégò i dalszégò rozwiju waji encyklopedie i mileczni, robòcy ferie, bò jes cëdownym editorã, chtëren chcôł zlepszëc i rëmąc wszelczé problemë nie leno techniczno, le téż òrtograficzno waji encyklopedie, żebë Kaszëbskô Wikipediô stała sã wikszim i encyklopedicznym placã pò kaszëbskù.
:PS. Przepraszam za błędy ortograficzne w języku kaszubskim, bo korzystałem słowniki.
:Gòrący pòzdrówk, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 15:51, 5 lëp 2026 (CEST)
</div>
c8fhtcjdmbrcqrr0aleztq2yx7e0s32
204465
204446
2026-07-05T15:30:20Z
Orbitminis
18250
/* Òddzãkòwanié */ Odpowiedź
204465
wikitext
text/x-wiki
<div align="center" class="usermessage">Witôjtaż w mòji diskùsji brëkòwnika! Mòżesz téż pisac pò kaszëbskù, pò pòlskù, pò anielskù i pò miemiecckù, a pònemù bãdã òdpòwiadał.
----[[Òbrôzk:Sandbox.svg|lewo|bezramki|78x78px]][[Brëkòwnik:Orbitminis/Zapisownik|<big>ZAPISOWNIK</big>]] [[Brëkòwnik:Orbitminis|<big>Ò MIE</big>]] [[Òbrôzk:Debate,_discussion.jpg|bezramki|195x195px]] '''<big>DISKÙSËJÔ BRËKÒWNIKA</big>'''
</div>
<div align="center" class="disscusion">
== Poprawanie linków ==
Hej. Nie musisz wykonywać takich poprawek, jak <tt><nowiki>[[Gduńsk]]ù -> [[Gduńsk|Gduńskù]]</nowiki></tt> – w najbliższych dniach (może tygodniach) konfiguracja linków zostanie naprawiona na poziomie całej wiki. Pozdrawiam, [[Brëkòwnik:Tsca|tsca]] ([[Diskùsëjô brëkòwnika:Tsca|diskùsëjô]]) 21:01, 31 zél 2025 (CEST)
:Wiem, proszę pana. Ale ja muszę poprawiać kaszubską Wikipedię, żeby nie były takich błędów ortograficznych jak "[[Gduńsk]]ù" i "Jop Polsczei jaczieego", bo te błędy mogą sprawiać kaszubską Wikipedię jako encyklopedię trzeciego świata jak najmniejsze Wikipedie w różnych językach. I oczywiście te źródła, artykuły, samouczki i szablony niektóre wyglądają staro jak w latach 2000-2010, więc muszą mieć nowe źródła, świeże infomacje i szablony podobne do polskiej Wikipedii po kaszubsku. Więc przepraszam najmocniej za takie, które spełniłem. Pozdrawiam, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 10:12, 1 Séw 2025 (CEST)
== Automatycznie tłumaczone artykuły ==
Wklejanie automatycznie tłumaczonych artykułów mija się z celem. Skoro, jak sam deklarujesz na swojej stronie użytkownika, nie znasz języka kaszubskiego, nie jesteś w stanie zauważyć ani poprawić błędów w maszynowo przetłumaczonych treściach. Hasła utworzone w ten sposób niestety muszą zostać usunięte. [[Brëkòwnik:Tsca|tsca]] ([[Diskùsëjô brëkòwnika:Tsca|diskùsëjô]]) 23:28, 29 ruj 2025 (CET)
:czemu? [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 16:11, 18 lës 2025 (CET)
::CSB: Mòjn panie Tsca! Dzysë jô sã dowiedzôł twòją nôùczkã, żëbë nie wlëmic "aùtomaticznie dolmaczonëch artiklów". Wiãc pòstanowił ùżëc słowôrza do stwórzbë artiklów, żëbë bëło trochã lepszé artiklów z pasëjącą pisënką. Żle w mojich artiklach nalézają sã felë, dôj mni znac. Pozdrôwióm, Orbitminis.
::PL: Cześć, panie Tsca! Dzisiaj ja się dowiedziałem twoją nauczkę, żeby nie wklejać „automatycznie tłumaczonych artykułów”. Więc postanowiłem użyć słownika do stworzenia artykułów, żeby było trochę lepsze artykułów z odpowiednią ortografią. Jeżeli w moich artykułów znajdują się błędy, daj mnie znać. Pozdrawiam,
::[[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:45, 9 gro 2026 (CET)
== Odpowiedź ==
Cześć, po pierwsze, wybacz, że nie zareagowałem na [[Diskùsëjô brëkòwnika:DawnyTest#Szablónë pòtrzébùją zmianë ë aktualizacëji|twoją starą wiadomość]] o dyskusji na naszej wiki. W pełni się zgadzam, iż zmienione muszą zostać szablony, artykuły itp. Próbowałem podjąć się [[Szablóna:Infobox pòlitik|trudu ich tworzenia]] i edytowania (konkretnie infoboksów). Takowym był ''[[Szablóna:Infobox-państwò]]'', który w mojej opinii posiadał pewne problemy, które udało mi się zaleczyć. Posiłkowałem się m.in. polską wiki i tak na przykład mamy tam rewolucyjny wynalazek opcjonalnych parametrów. Po drugie, odnosząc się do twojej wiadomości, czy jest coś konkretnego co, twoim zdaniem, dobrze by było zmienić w szablonach lub ewentualnie by jakieś szablony utworzyć? Jestem chętny by podjąć się współpracy. Pozdrawiam cieplutko.<br>
<small>A po polsku odpisałem, bo mi było szybciej, nie znam kaszubskiego na tyle, by bezbłędnie wytworzyć taką wiadomość w tak krótkim czasie.</small> [[Brëkòwnik:DawnyTest|DawnyTest]] ([[Diskùsëjô brëkòwnika:DawnyTest|diskùsëjô]]) 21:25, 18 str 2026 (CET)
== Pòdzãkòwanié a pëtanié ==
Laudetur Iesus Christus et Semper Virgo Maria!
Jô bëm chcôł Cë baro pòdzãkòwac za docenienié mòji robòtë z ùsôdzaniém wiadłokôstków dlô kaszëbsczi Wikipedëje a przëznanié mie pòczestny gwiôzdë. Hewò je dlô mie wiôlgą bùchą, eż jô mògã na taczé mòdło czënic nen môl jesz lepszim a snôższim. Takô deja je mie blizą òb robienié nowich szablonów a robòtã nad artiklama, co do jaczich wiedno jô móm starã, abë bëłë jak nôkwalitetniészé ë jak nôbarżi rozkòscérzóné.
Przi ti lëżnoscë jô bëm chcôł Cë ò jedno zapëtôc. Jô trafił niedôwno na twòją starnã brëkòwnikã ë jô móm wiôldżé ùwôżanié do tewò, jak Të fejn to zrobił. Mògã jô ùżëc twòjéwò kòdù do swòjich starnów brëkòwnikã. Jô w nôblëższim czasu chcã zabrac sã za ji ùsôdzenié na pòlsczi a anielsczi Wikipedëjë, a téż jô nie òdsôdzôm ùprawieniégò ë przebùdérowaniéwò téż mòji starnë brëkòwnika z kaszëbsczi Wikipedëjë. Jô z górë dzãkùjã, pòzdrówiôm a żëczã bëlni, ùbëtni nocë. Òstani z Bògã!
PS. Jakbyś czegoś nie rozumiał z tego, co wyżej napisałem, śmiało napisz, a ja to ci to napiszę po polsku. [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 01:29, 20 łżë 2026 (CEST)
:Mòjn ë ni mô sprawë, panie Jakùbie! Przédnã przëczënã, dlôcze przëznôł pòczestny gwiôzdë za stwòrzenié zacht szablónów i zbògacënié kaszëbsczi Wikipedie je, że w kaszëbsczi Wikipedie, tak jak wszelejaczé wersje môlowëch jãzëków nicht nie przëznôlë zwëkòwich abò wiãkszëch brëkòwników nôdgrodë, żebë mòtiwòwac i òstac na długò, a pózni przestôlë prôcowac dlô kaszëbsczi Wikipedie i òpùscëc to całowno. Dlôtegò pòstanowił przëznac dlô Cebie gwiôzdë, żëbë mòtiwòwac i òstac na długò. Òczëwisce ni mògã dużo gadac z tim, co napisôł. Bóg zapłac za stwòrzenié dlô mie diskùsje, bò z tim mie ceszy :) I żëczë bëlno zdrowiégò i ùcemiãgã za stwòrzenié taczé fejn cuda. (wëbaczë za pisënkòwé felë) Gòrąco pòzdrôwióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:05, 20 łżë 2026 (CEST)
::Jô téż pòzdrówiôm. Le co do mòjégò pëtaniégò niżi, wëdôlmaczã sprawã pò pòlskù, abë bëło lżi to bëlno zrozmiec.
::Zauważyłem, że fajnie zbudowałeś swoją stronę użytkownika na kaszubskiej Wikipedii. Bardzo mi podoba rozwiązanie, jakiego użyłeś, by to usystematyzować. Pytałem zaś o to, czy mógłbym w przyszłości użyć twojego kodu przy tworzeniu moich osobistych stron użytkownika? Myślę tu głównie o stronie na polską Wikipedię (cóż, jako że tam również coś czasem napiszę lub zedytuję, wypadałoby, abym w końcu ją stworzył XD),oraz anglojęzyczną, ale nie wykluczam też w przyszłości przebudowy tej już stworzonej przeze mnie na kaszubskiej Wikipedii. [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 13:05, 21 łżë 2026 (CEST)
== Pòdzãkòwanié za przeniosbã ==
Wiôldżé Bóg zapłac Cë za przeniesenié artikla ò Mòdżorów Pioterze na jegò docélowi môl. Jô jem baro rôd, eż mòja robòta je òbzéranô a docenianiô bez jinszich wikipedistów. Z drëdżi starnë jô baro przeprôszóm za felënk zdrzódłów. To, eż jô jesz jich ni miôł dodóné, bëło jedurnym pòwòdã, dlô jaczégò articzel béł jesz w zapisownikù. Wczorô miôł jem to wnetka zrobióné, le pò czilkù gòdzënach zmërkôł jem, eż jô nie zapisôł wińdzeniégò swòji robòtë, a jakno eż mòji laptop sã aùtomaticzno zrestartowôł, stracëł jem wszëtczé brzôdë mòji robòtë z ùzdrzódłowianiém. Terôzka jô mdã te zdrzódła dodawac òd zôczątkù a jô mdã miec starã, abë jesz dzys ònë bëłë dodóné. Jesz rôz dzãkã a żëczã jem milëczni majówczi. :) [[Brëkòwnik:RemusWej|RemusWej]] ([[Diskùsëjô brëkòwnika:RemusWej|diskùsëjô]]) 12:17, 1 môj 2026 (CEST)
:Mòjn ë ni mô sprawë! Przédnã przëczënã, dlôcze przeniosł twój zapisownik-articzel ò Mòdżorów Pioterze na jegò docélowi môl, to że twój zapisownik wëzdrzi jak na dłudżi i òsoblëwi, pòniewôż w kaszëbsczi Wikipedie mómë za mało dłudżich articzlów, bò ni chcą pòswiãcëwac przënômni 30% czasu na editowanié i dofùlowiwanié kaszëbsczich rzeczy, dopòczi staną sã niaktualné i stôré. Dlôte pòstanowił to robic. Òczëwisce w dodôtkù dodôł wiki-fónkcje taczich jak Commons, kategòrie i zacht jinszëch i kòrektowôł je. Nic sã ni stało, że ni dodôł u Cebie zdrzódłów, bò nierôz mògą bëc problemë. Òczëwisce czejbë chcesz zdolmaczëc interfejs MediaWiki na kaszëbsczi (bò stwòrzëł w Karczmie diskùsje), klëkni tuwò: [https://translatewiki.net TranslateWiki]! Dzãka za stwòrzenié diskùsje ò mie :) Cebie téż żëczã jem milëczni majówczi i Òstani z Bògã! Pòzdrówióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 12:59, 1 môj 2026 (CEST)
== Słów czile ==
Mòjn! Miło je widzec, że persónë taczé jak të, chcą sã angażowac i robic dlô kaszëbiznë na tak apartny métel jak rozwij Wikipedie – móm dlô ce bédënk, cobë më z @[[Brëkòwnik:Rzadtymczasowy|Rzadtymczasowy]] mòglë bëc z Tobą w łączbie, pòmagac (np. jinyma słowarzama niż niedoskònałi sloworz.org) i dzejac mët, môsz të Discorda abò cos jinszégò do kòmùnikacje? [[Brëkòwnik:Terrus38|Terrus38]] ([[Diskùsëjô brëkòwnika:Terrus38|diskùsëjô]]) 15:35, 15 cze 2026 (CEST)
:Witam, panie Terrus! Na razie nie będę mówił po kaszubsku, bo słowniki kaszubskie w internecie są jeszcze ograniczone, ale na końcu wstawię pozdrowienie i podziękowanie do Ciebie. Dziękuję za poprawki gramatyczne moich tłumaczeń w TranslateWiki. I oczywiście odkąd zrobiłeś dzisiaj dyskusję, tak, ja zaangażowałem do dyskusji dotyczące gramatyki kaszubszczyzny, bo pan i twoi koledzy robicie małą debatę. Mówiąc zaangażowaniu, będę podawał kroki:
:# Nie posiadam żadnych komunikatorów, bo komunikatory według mnie są nieprzydatne i niepotrzebne, e-mail też.
:# Na razie wolę robić edycje w kaszubskiej Wikipedii i je rozbudować.
:PS: Napisałem odrobinę profesjonalnie, bo tak muszę. Dzãkùjã baro za stwòrzenié diskùsje dlô mie i serdeczno pòzdrówióm, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 16:22, 15 cze 2026 (CEST)
</div>
== Òddzãkòwanié ==
Adjé, Orbitminis, dzãka za Twój wkłôd we Wikipediã i żëczã wszëtczégò bëlnégò! Jô ale prosto pòprôwiôł pisënk a sztilistikã pò pòprzédnëch edicjach, jô nie rëmôł bezpòtrzébno akapitów encyklopedicznégò tekstu, jaczi pisalë jiny, czë Të, czë jinszi. Jô nie jem Tobie za wroga, a za wespółrobòtnika. W kòżdim razu, pòzdrówk. [[Brëkòwnik:Terrus38|Terrus38]] ([[Diskùsëjô brëkòwnika:Terrus38|diskùsëjô]]) 14:50, 5 lëp 2026 (CEST)
:Witôjtaż baro serdeczno, panie Terrusie!
:Ostatnio zakończyłem/zawiesiłem moją działalność na zawsze ze względu, że nie chciałem przez całe lato i życie edytować cały czas na kaszubskiej Wikipedii z powodu znajomości i wolnego stylu edytowania korzystając ze słowników takich jak wspomniany przez Ciebie wcześniej „niedokładnego [[Internetowi Słowôrz Kaszëbsczégò Jãzëka|Internetowégò Słowarza Kaszëbsczégò Jãzëka]]” i Glosbe (przejście polsko-kaszubskie). Ja chciałem tylko zbogacić, a unowocześnić waszą encyklopedię polegając informacji z polskiej Wikipedii jako hobby albo cel do uratowania mniejszościowych języków Polski (ostatnio rozszerzyłem informacje o religijnych postaciach [[Mirosłôw Adamczik|Mirosława Adamczyka]] i [[Wòjcech Cëchòsz|Wòjcecha Cëchòsza]]) i podczas wybuchu ilości moich wkładów dodałem sporo własnych i samodzielnych informacji w artykule [[Méster Bëlnégò Czëtaniô (kònkùrs)|Méster Bëlnégò Czëtaniô]] z przepisami. I nie tylko poprawiłeś niektóre moje edycje, ale także użyłeś mojego userboksa (potocznie kaszubskie: ''brëkòkôstka'') dotyczące o małą ilością artykułów w waszej encyklopedii przed usunięciem mojej strony, ale mimo wszystkiego, czasami albo rzadko robię problemy z przeniesieniami i ortografią ze względu trudności, leksyki i mniejszą ilością zasobów kaszubskiego języka w Internecie (mimo że np. w Youtubie jest ich dużo filmików i sporo kanałów po kaszubsku).
:Dziękuję panu za twoje pańskie miłe słowa, stworzenie dyskusji i uznawanie mnie za współpracownika, bo spędziłem dokładnie rok i pół czasu, żeby zbudować i poświęcić na tworzenie moich tłumaczeń kaszubskich do artykułów i szablonów! (przełączę na kaszubski) Cebie téż żëczã wszëtczégò bëlnégò i dalszégò rozwiju waji encyklopedie i mileczni, robòcy ferie, bò jes cëdownym editorã, chtëren chcôł zlepszëc i rëmąc wszelczé problemë nie leno techniczno, le téż òrtograficzno waji encyklopedie, żebë Kaszëbskô Wikipediô stała sã wikszim i encyklopedicznym placã pò kaszëbskù.
:PS. Przepraszam za błędy ortograficzne w języku kaszubskim, bo korzystałem słowniki.
:Gòrący pòzdrówk, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 15:51, 5 lëp 2026 (CEST)
::Czekaj! Jeszcze jedno miałem powiedzieć Jeśli są problemy i nadchodzące wkłady, może za jakiś czas może wrócę do Kaszubskiej Wikipedii. Ale teraz już będę, więc to nie będzie pożegnanie. [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 17:30, 5 lëp 2026 (CEST)
9s64qf0rw8dbo8qbg4wxsqy5l86r5dy
Òstrowë Salomóna
0
12399
204509
201370
2026-07-06T11:52:19Z
Iketsi
3254
{{jãz.|xx}}
204509
wikitext
text/x-wiki
{{Infobox-państwò|gwôsné miono=Solomon Islands|miono=Òstrowë Salomóna|fana=Flag of the Solomon Islands.svg|herb=Coat of arms of the Solomon Islands.svg|miono-genitiw=Òstrowów Salomóna|mòtto=To Lead is to Serve (Prowadzëc znôczô służëc)|na karce=Solomon Islands on the globe (Oceania centered).svg|jãzëk=[[anielsczi jãzëk|anielsczi]]|stolëca=Honiara|fòrma państwa=Kònstitucëjnô mònarchijô|wiéchrzëzna=28 450|rok=2025|lëdztwò=838,645|dëtk=dolar Òstrowów Salomóna|kòd dëtka=SBD|czasowô cona=+11|swiãto=[[7 lëpińca]]|himn=God Save Our Solomon Islands|kòd=SB|Internet=.sb|telefón=677|Premiéra=Jeremiah Manele|Prezydeńt=David Tiva Kapu|Mònarcha=Karól III|data ùsôdzenia=7 lëpińca 1978|aùtowi kòd=SOL}}
'''Òstrowë Salomóna''' ({{jãz.|pl}} ''Solomon Islands'') sã państwã na òstrowach w [[Òceaniô|Oceanije]], w Melanezji, nad [[Spòkójny Òcean|Spòkójnym Òceanã]]. Bëłë kòlonią [[Wiôlgô Britanijô|Wiôldżi Britanji]], zwëskałë samòstójnotã 7 lëpińca 1978<ref>[https://www.spc.int/events/solomon-islands-independence-day-2025 Solomon Islands independence day 2025]</ref>.
== Geògrafiô ==
* Pòłożenié: Krôj leżë na òstrowach na pôłniowo-zôpadnym [[Spòkójny Òcean|Spòkójnym Òceanã]], 2000 km òd nordowo-pòrënkowi [[Aùstralëjô|Aùstralëji]]. Państwò cignje sã na òk. 1000 òstrowów (w tim 9 przédnich), stolëca (Honiara) leżë na nôwikszim òstrowie Guadalcanal<ref name=":0">[https://www.dfat.gov.au/geo/solomon-islands/solomon-islands-country-brief Solomon Islands country brief] Australian Government</ref>
* Wiéchrzëzna:
** całownô: 28,896
** wiéchrzëzna lądu: 27,986
** wiéchrzëzna wòdów: 910
* Brzegòwô liniô: 5,313
* Klimat: tropikalny mónsunowi, niewiele skrajnëch cepłot ë pògòdowëch warënków
* Ùsztôłcenié terenu: przédno sërowe górë, z czilema nisczimi kòralowëmã atolamã
* Wësokòsc terenu:
** nôniższô wësokòsc: 0 (Spòkójny Òcean)
** nôwëższi czëp: 2,335 (czëp Popomanaseu)
== Demografiô ==
* Lëdztwò: 838,645 (2025)<ref>[https://www.worldometers.info/world-population/solomon-islands-population/ Solomon Islands Population <small>(LIVE)</small>] Worldometer</ref>
* Etniczné karna: W wikszosci Melanezijczicë (93%), w mniejszosci Polinezijczicë (4%), Micronezijczicë (1.5%), Chińczicë, Europejczicë ë jinszi (1.5%)<ref name=":1">[https://www.un.int/solomonislands/solomonislands/country-facts Country Facts | Solomon Islands] The Permanent Mission of Solomon Islands to the United Nations</ref>.
* Jãzëczi: Òficjalny je anielsczi jãzek, le je też 63 jinszich<ref name=":0" />.
* Religijné karna: nôwikszą religią je [[Chrzescëjanizna|Chrzescejanizna]] (95%), w tim Kòscół Melanezji (anglikańsczi) 29%, [[Katolëcczi Kòscół]] 19%, Ewangelëcczi Kòscół Pôłniowego Mòrza 17%, Metodëscë 11%, Adwentiscë Sódmégo Dnia 10%<ref name=":1" />
* Ùrbanizacjô: Wikszosc mieszkańców żëje na wiesczi terenach, òk. 73.5%<ref>[https://www.theglobaleconomy.com/Solomon-Islands/population_size/ Solomon Islands: Population size] Global Economy</ref>
== Pòliticzny system ==
* fòrma państwa: kònstitucëjnô [[mònarchiô]]
* Wëkònôwczô władza: król (Karól III<ref>[https://solomons.gov.sb/government/ About the Solomon Islands Government] Solomon Islands Government</ref>), reprezentowany przez gùbernatóra (David Tiva Kapu<ref>[https://solomons.gov.sb/governor-general-leaves-for-knighthood-in-london/ Governor General leaves for knighthood in London] Solomon Islands Goverment</ref>) ë premiera (Jeremiah Manele<ref>[https://forumsec.org/forum-leaders/hon-jeremiah-manele The Hon Jeremiah Manele] Pacific Islands Forum</ref>)
* Ùstawòdôwczô władza: jednojizbowi parlament, 50 nôleżników wëbieranëch na sztërëlatną kadencjã<ref>[https://parliament.gov.sb/about/parliament/ About] National Parliament of Solomon Islands</ref>.
== Òbaczë téż ==
* [[Aùstralëjô (kòntinent)]]
== Przëpisë ==
{{Przëpisë}}
== Bibliografiô ==
* [https://www.cia.gov/the-world-factbook/countries/solomon-islands/|The World Factbook]
== Bùtnowé lënczi ==
{{Commons}}
{{Aùstralëjô i Òceaniô}}
{{Kòntrola}}
[[Kategòrëjô:Państwa w Aùstralëji i Oceanije]]
cmf3f8tv98ucnl6cxmzxiynzzf6fob5
Diskùsëjô brëkòwnika:Ясамойла
3
12915
204496
200107
2026-07-06T11:10:39Z
Orbitminis
18250
/* */ Odpowiedź
204496
wikitext
text/x-wiki
Witóm serdeczno w '''kaszëbsczi Wikipedie''', pónie Ясамойла! [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 17:43, 12 môj 2026 (CEST)
:Дзякуй. [[Brëkòwnik:Ясамойла|Ясамойла]] ([[Diskùsëjô brëkòwnika:Ясамойла|diskùsëjô]]) 17:46, 12 môj 2026 (CEST)
::(Przepraszam, że piszę po polsku, bo nie umiem pisać po białorusku!) Nie ma sprawy, panie Ясамойла! Dziękuję za stworzenie szablonów Wieży Babel i wznoszenie wkładów do kaszubskiej Wikipedii! Ponieważ mi się cieszy. Więc postanowiłem to powiedzieć o tych powodach. Dlaczego chciałem się powitać Pana po kaszubsku? W kaszubskiej Wikipedii nie posiadamy przynajmniej szablonu do powitania użytkowników po kaszubsku, ponieważ administratorzy kaszubskiej Wikipedii Tsca, Kirsan i Kaszeba są zajęci lub emerytowani od Wikipedii, a kolejnym: Dlaczego tworzysz szablony Wieży Babel? Ponieważ w kaszubskiej Wikipedii jest obecnie za mało szablonów Babel z powodu nieaktywnych użytkowników, bo nie chcą poświęcić dużo czasu na tworzenie szablonów i wkładów. Jeśli tekst jest niezrozumiały lub napisany w obcym języku, użyj tłumacza oraz rozszyfruj tekst. Dziękuję za uwagę.
PS. Ja też stworzyłem szablon Wieży Babel, ale do języka rosyjskiego. Pozdrawiam, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 19:04, 12 môj 2026 (CEST)
:Добра. [[Brëkòwnik:Ясамойла|Ясамойла]] ([[Diskùsëjô brëkòwnika:Ясамойла|diskùsëjô]]) 17:20, 20 môj 2026 (CEST)
::I oczywiście nie wiedziałem, że w cerkiewnosłowiańskiej Wikipedii (https://cu.wikipedia.org/wiki/%D0%93%D0%BB%D0%B0%D0%B2%D1%8C%D0%BD%D0%B0_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0) jesteś jeden z największych edytorów tej małej encyklopedii i deklarujesz języka cerkiewnosłowiańskiego (''Словѣньскъ ѩꙁꙑкъ'') jako ojczysty język ze względu, że chodzisz do cerkwi codziennie lub zapamiętałeś cały wschodniosłowiański język liturgiczny na pamięć :0
::Przecież język cerkiewnosłowiański już wymarł setki lat temu i nie posiadał żaden ojczystych użytkowników, czyli, tak czy siak, to mogło być błąd, bo [[Internet]] nas oszukał.
::ꙁ богѣмъ ☦️🙏. [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 13:10, 6 lëp 2026 (CEST)
ib9s6gpljk5jrhdidqjwfv69j8znzzb
204497
204496
2026-07-06T11:12:42Z
Orbitminis
18250
204497
wikitext
text/x-wiki
Witóm serdeczno w '''kaszëbsczi Wikipedie''', pónie Ясамойла! [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 17:43, 12 môj 2026 (CEST)
:Дзякуй. [[Brëkòwnik:Ясамойла|Ясамойла]] ([[Diskùsëjô brëkòwnika:Ясамойла|diskùsëjô]]) 17:46, 12 môj 2026 (CEST)
::(Przepraszam, że piszę po polsku, bo nie umiem pisać po białorusku!) Nie ma sprawy, panie Ясамойла! Dziękuję za stworzenie szablonów Wieży Babel i wznoszenie wkładów do kaszubskiej Wikipedii! Ponieważ mi się cieszy. Więc postanowiłem to powiedzieć o tych powodach. Dlaczego chciałem się powitać Pana po kaszubsku? W kaszubskiej Wikipedii nie posiadamy przynajmniej szablonu do powitania użytkowników po kaszubsku, ponieważ administratorzy kaszubskiej Wikipedii Tsca, Kirsan i Kaszeba są zajęci lub emerytowani od Wikipedii, a kolejnym: Dlaczego tworzysz szablony Wieży Babel? Ponieważ w kaszubskiej Wikipedii jest obecnie za mało szablonów Babel z powodu nieaktywnych użytkowników, bo nie chcą poświęcić dużo czasu na tworzenie szablonów i wkładów. Jeśli tekst jest niezrozumiały lub napisany w obcym języku, użyj tłumacza oraz rozszyfruj tekst. Dziękuję za uwagę.
PS. Ja też stworzyłem szablon Wieży Babel, ale do języka rosyjskiego. Pozdrawiam, [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 19:04, 12 môj 2026 (CEST)
:Добра. [[Brëkòwnik:Ясамойла|Ясамойла]] ([[Diskùsëjô brëkòwnika:Ясамойла|diskùsëjô]]) 17:20, 20 môj 2026 (CEST)
::I oczywiście nie wiedziałem, że w cerkiewnosłowiańskiej Wikipedii (https://cu.wikipedia.org/wiki/%D0%93%D0%BB%D0%B0%D0%B2%D1%8C%D0%BD%D0%B0_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0) jesteś jeden z największych edytorów tej małej encyklopedii i deklarujesz języka cerkiewnosłowiańskiego (''Словѣньскъ ѩꙁꙑкъ'') jako ojczysty język ze względu, że chodzisz do cerkwi codziennie lub zapamiętałeś cały wschodniosłowiański język liturgiczny na pamięć :0
::Przecież język cerkiewnosłowiański już wymarł setki lat temu i nie posiadał żaden ojczystych użytkowników, czyli, tak czy siak, to mogło być błąd, bo [[Internet]] nas oszukał.
::ꙁ богѣмъ ☦️🙏 : [[Brëkòwnik:Orbitminis|Orbitminis]] ([[Diskùsëjô brëkòwnika:Orbitminis|diskùsëjô]]) 13:10, 6 lëp 2026 (CEST)
strax5zaj6k5i419zol15skokb9jm8w
Brëkòwnik:Rabùszniczka/zapisownik
2
13080
204469
203359
2026-07-05T15:48:55Z
Rabùszniczka
19710
204469
wikitext
text/x-wiki
== Je to mój zapisownik, proszã mie tuwò nie zazérac i nick nie pisac! ==
== Òlga Bòznańskô ==
>tuwò mdzë infòbòks jak Michôł zrobi<
Òlga Helena Karolëna Bòznańskô (ùr. 15 łżëkwiata 1865 w Krakòwie, zm. 26 rujana 1940 w Pariżu) - pòlskô malarkô, pòrtecëstka epòczi mòdernizmù; nôleżniczka mònachijsczi szkòłë, Karna Pòlsczich Artistów "Sztuka" (1898) i Société Nationale des Beaux-Arts (1901).
== Kania ==
'''Kanie''' abò '''kùlindżi''' (''Milvus'') – pòzwa na ôrt drapiérzëch ptôchów z rodzëznë [[Accipitridae|jastrzibòwatëch]] (Accipitridae), jakô w kaszëbsczim jãzekù òdnosy sã do rodzajów [[Milvus]] (do jaczégò nôleżą [[lësatô kania]], [[czôrnô kania]], episkô kania), ''[[Haliastur]], [[Ictinia]] (''kanialuczi'').''
=== Kanie w Kaszëbsczi kùlturze. ===
>tuwò napiszã ò pòzdrzatkù na kanie ù Zëchtë i ò scynaniu kanie<
== Lësatô Kania ==
'''Lësatô kania''' (''Milvus milvus'') – gatënk drapieżnégò ptôcha z rodzëznë [[Accipitridae|Jastrzibòwatëch]]. Gatënk ten dzysdnia gniôzdëje w Eùropie, chòc w dôwnoce rozmnażôł sã téż w zôpadny Azji i północno-zôpadny Africe<ref>BirdLife International (2020). "Milvus milvus". IUCN Red List of Threatened Species. 2020 e.T22695072A181651010. doi:10.2305/IUCN.UK.2020-3.RLTS.T22695072A181651010.en. Retrieved 19 November 2021.</ref>.
=== Systematika ===
Lësatô kania òstała òpisónô przez Szwedzczégò rôdnika [[Karol Linneùsz|Karla Linneùsza]] w 1758 w dzesąti edycje jegò dokôzu ''Systema Naturae'' pòd binominalną gatënkòwą pòzwą ''Falco milvus''<ref>[[Karol Linneùsz|Linneùsz, Karol]] (1758) [https://www.biodiversitylibrary.org/page/726996#page/106/mode/1up Systema naturae per regna tria naturae, secundum classes, ordines, genera, species, cum characteribus, differentiis, synonymis, locis. Tomus I. Editio decima, reformata] (w łacëznie), Tom pierszi, Vol. 1. Stockholm: Laurentius Salvius. p. 89. "F. cera flava, cauda forsicata, corpore ferrugineo, capite albidiore."</ref>. Słowò ''Milvus'' je łaceńską pòzwą na tegò ptôcha. W 1799 francëzczi rodnik Bernard Germain de Lacépède przeniósł gatënk do rodz''aju Milvus.''
Wëapartniwóné są dwa pòdgatënczi Milvus milvus
* ''M. milvus milvus'' <small>(Linnaeus, 1758)</small> – '''lësatô kania'''
* ''M. milvus fasciicauda'' <small>E. Hartert, 1914</small> – '''zelonogłownô kania''' – endemit Òstrowów Zelony Głowë, prawie na gwës wymarłi
W rodzaju ''Milvus'' są téż dwa jinszé gatënczi: czôrnô kania i egipskô kania. Znóné są przëtrôfczi ùdany hybridizacji midzë lësatima i czôrnyma kaniami.
=== Hybridizacjô ===
Rodzôj ''Milvus'' zawierô w so jesz dwa jinszé gatënczi - Czôrną kaniã i Egipską kaniã<ref>Gill, Frank; Donsker, David, eds. (2018). [http://www.worldbirdnames.org/bow/raptors/ "New World vultures, Secretarybird, kites, hawks, eagles]". World Bird List Version 8.2. International Ornithologists' Union. Retrieved 24 September 2018.</ref>. Są znóné przëtrôfczi w jaczich lësatim kanióm ùdało sã rozmnożëc z czôrnyma kaniamim, dzało sã to w niewòli jak i dzëczim stónie jak np. na Òstrowach Zelony Głowë.
Strefa hybridizacje w jaczi òba gatënczi òkazyjnie sã krziżują jistnieje w Centralny Eùropie.<ref>Heneberg, Petr; Dolinay, Matej; Matuší, Hynek; Pfeiffer, Thomas; Nachtigall, Winfried; Bizos, Jiri; Šimčiková, Daniela; Literák, Ivan (2016). [https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0159202 "Conservation of the Red Kite Milvus milvus (Aves: Accipitriformes) Is Not Affected by the Establishment of a Broad Hybrid Zone with the Black Kite Milvus migrans migrans in Central Europe"]. PLoS ONE. 11 (7). Retrieved 30 August 2025.</ref> Geneticzné badérowaniô kaniów z ti rëmi nie wëkazałe niżódnégò [[mtDNA]] czôrnëch kaniów w lësatich kaniach i vice versa. Przez to, że ten ôrt DNA je przekazëwóny przez nënczi, aùtorzë badaniô sùgerują, że mieszóné samice są bezpłodné w zgòdze z [[Prawò Haldana|prawã Haldana]].
W 2006 rokù w Szkòcji doszło do ùdanégò rozrodu samca czôrny kanie z samicą lësowati kanie w jaczim ùdało jim sã wëchòwac dwa pòdlotë.
=== Charakteristika ===
Ptôch mô wësmùkłą sylwetkã i pstrokaté pióra. Jegò cało mô 60 - 72 cm długòscy<ref>Orta, J., Christie, D.A. & Garcia, E.F.J.: [https://www.hbw.com/species/red-kite-milvus-milvus Red Kite (Milvus milvus)]. [w:] del Hoyo, J., Elliott, A., Sargatal, J., Christie, D.A. & de Juana, E. (eds.). Handbook of the Birds of the World Alive.</ref>, i 175 - 195 cm w rozpiertoscy lecëdłów. Samce ważą òd 800 do 1200 gramów, timczasã samice są wiãkszé ważą midzë 1000, a 1300 gramów. Pòza różnicą wôdze midzë płcama ptôcha ni ma wiãkszich różniców - òbie płce są szlachòwno ùbarwióné - mają szarawą głowã, przeważnie rdzawé cało z cemniejszima plecama i białima akcentama na lëcadłach. W przerównanim do czôrny kani, lësatô kania je kąsk wiãkszô i ma charakteristiczny mòcno wcãti ògón (niżóden drapieżnik Eùropë mô jaż tak mòcné wcãce jak lësatô kania). W pòrównaniu z mëszołowã je wiãkszô i mô dłuższé lëcadła.
=== Wëstãpòwanié: ===
Lësaté kanie są ptôchama jaczé zamieszkùją lëstowaté lasë, łączi, pòla dolenë i sampòwaté pògreńcza. Pòchòdzą z zôpadny Palearktyczny krajinë, tak tej wszëtczé znóné rodné parë (jaczich je kòl 32,200 - 37,000) gniôzdëją w Eùropie.
Przede wszëtczim żëje w zôpadny, strzôdkòwi i pôłniowi Eùropie, nielëcznie téż w wschòdnym parce Azji Mniszi i w północny Africe, jaczi status nie je gwësny - lãgòwô pòpùlacja z Maroka je ùznôwónô za wëmarłą, chòc sómé kanie dali zëmùje w tim kraju. Dawni gatënk mòżna bëło pòtkac téż na Òstrowach Kanarijsczich i Òstrowach Zelonégò Przëlądka(?) gdze prawie na gwës wëmôrł<ref>Garcia-del-Rey, E. (2011). Field Guide to the Birds of Macaronesia. Azores, Madeira, Canary Islands, Cape Verde. Lynx Edicions, Barcelona, Spain.</ref>. Kania je wãdrownô, chòc to zanôlégô òd ji pòpùlacji - ptôchë z pôłni mieszkają ù se całi rok, a te żëjącé w ùmiarkòwónym klimace migrëją òd zélnikã do séwnikã nad Strzôdzemné Mòrze, abë wrócëc do swòjich gniôzd kòl strëmiannikã. W przërównanim do [[Czôrnô kania|czôrny kanie]] ji zasyg je barżi ògraniczony.
f1bkqegdgzzzcg6ye20q3pbavrozu50
Brëkòwnik:Terrus38
2
13091
204443
204368
2026-07-05T13:08:36Z
Jakkonles12
8987
204443
wikitext
text/x-wiki
== Jô ==
{{Babel|pl|csb-4|en-3|lv-3|sr-3|de-2|ru-1}}Pòmòrzón, lubòtnik jãzëków, rézów, jazdë na kòle, pòliticzi a wiele jinëch. M.jin. sztudéra kaszëbsczi etnofilologie, w 2025 r. dobiwca Kaszëbsczégò Diktanda. Pòzdrówk, '''make csbwiki great''' again (nawetka nié great again, prosto great)!
== Wkłôd ==
Nôwikszi wkłôd miôł jem w ne starnë: [[Ùskô]], [[Brainstorm (mùzyczné karno)]], [[Benedikt XVI]], [[Szablóna:Mùzyczné karno infòbòks]], ale dokłôdóm pò përznie do wiele wicy artiklów, òsoblëwie zajimóm sã kòrektą.
== Jiné ==
{{Brëkòkôstka|[[Òbrôzk:White-blue-white flag.svg|40px|border]]|{{gender:{{BASEPAGENAME}}|Ten Brëkòwnik|Ta brëkòwniczka|Ten brëkòwnik}} popiérô biôtkã ò [[Demòkracjô|demòkracjã]] w [[Ruskô|Rusce]].<br />Свобода России!|tło1=blue|tło2=white}}{{Brëkòkôstka|[[Òbrôzk:Flag_of_Belarus_(1918,_1991-1995).svg|40px|border]]|{{gender:{{BASEPAGENAME}}|Ten brëkòwnik|Ta brëkòwniczka|Ten brëkòwnik}} popiérô biôtkã ò [[Demòkracjô|demòkracjã]] na [[Biôłorus|Biôłorusë]].<br />Жыве Беларусь!|tło1=red|tło2=white}}{{Brëkòkôstka|[[Òbrôzk:P human body.svg|40px]]|{{gender:{{BASEPAGENAME}}|Ten brëkòwnik mô sã za '''[[Hùmanistika|humanistã]]'''.|Ta brëkòwniczka mô sã za '''[[Hùmanistika|humanistkã]]'''.}}|tło1=white|tło2=#D8BFD8|ramka=black}}{{Brëkòkôstka|[[Òbrôzk:Palestine Waving Flag Icon.svg|48px|Palestine Waving Flag Icon]]|{{gender:{{BASEPAGENAME}}|Ten wikipedista|Ta wikipedistka}} <b>pòpiérô</b> biôtkã palestińsczégò nôrodu ë sprocëmniwô sã lëdobójstwù w Conie Gazë|tło1=#009000|tło2=#008000|kolor2=white}}{{Brëkòkôstka|[[Òbrôzk:Bicycle-icon.png]]|{{gender:{{BASEPAGENAME}}|Ten brëkòwnik|Ta brëkòwniczka}} rôd [[rekreacjô|rekreacjowò]] jezdzy na [[Kòło|kòle]].|tło1=white|tło2=lightgrey}}{{Brëkòkôstka|tło1=#FEAAAA|tło2=white|[[Òbrôzk:Anticapitalismo.svg|45px]]|Ten brëkòwnik jest procëmny wprowadzeniu [[eùro]]. Je òn dbë, że mòże pò wprowadzeniu ti walutë swiat sã nie zawôli, le równak Polskô stracy cësk na mònetarną pòlitikã w gwôsnym kraju.}}{{Brëkòkôstka|{{NUMBEROFARTICLES}}|Nen brëkòwnik mëszli, że kaszëbskô Wikipediô mô za mało artiklów.|tło1=#7E7CF2|tło2=#4CEB3F}}{{Brëkòkôstka
| ramka = Black
| tło2 = #CC0000
| tło1 = White
| farwa2 = White
| 1 = [[Òbrôzk:Grammar Nazi Icon Text.svg|80px]]
| 2 = Ten brëkòwnik je òrtograficznym faszistą.
}}
dv9i9d6qb9t3h27emjzrmfy3kntblce
204444
204443
2026-07-05T13:26:10Z
Terrus38
14026
ej ale jem òrtograficznym faszistą, nie jem bezznankòwnikòwim faszistą, ok?
204444
wikitext
text/x-wiki
== Jô ==
{{Babel|pl|csb-4|en-3|lv-3|sr-3|de-2|ru-1}}Pòmòrzón, lubòtnik jãzëków, rézów, jazdë na kòle, pòliticzi a wiele jinëch. M.jin. sztudéra kaszëbsczi etnofilologie, w 2025 r. dobiwca Kaszëbsczégò Diktanda. Pòzdrówk, '''make csbwiki great''' again (nawetka nié great again, prosto great)!
== Wkłôd ==
Nôwikszi wkłôd miôł jem w ne starnë: [[Ùskô]], [[Brainstorm (mùzyczné karno)]], [[Benedikt XVI]], [[Szablóna:Mùzyczné karno infòbòks]], ale dokłôdóm pò përznie do wiele wicy artiklów, òsoblëwie zajimóm sã kòrektą.
== Jiné ==
{{Brëkòkôstka|[[Òbrôzk:White-blue-white flag.svg|40px|border]]|{{gender:{{BASEPAGENAME}}|Ten Brëkòwnik|Ta brëkòwniczka|Ten brëkòwnik}} popiérô biôtkã ò [[Demòkracjô|demòkracjã]] w [[Ruskô|Rusce]].<br />Свобода России!|tło1=blue|tło2=white}}{{Brëkòkôstka|[[Òbrôzk:Flag_of_Belarus_(1918,_1991-1995).svg|40px|border]]|{{gender:{{BASEPAGENAME}}|Ten brëkòwnik|Ta brëkòwniczka|Ten brëkòwnik}} popiérô biôtkã ò [[Demòkracjô|demòkracjã]] na [[Biôłorus|Biôłorusë]].<br />Жыве Беларусь!|tło1=red|tło2=white}}{{Brëkòkôstka|[[Òbrôzk:P human body.svg|40px]]|{{gender:{{BASEPAGENAME}}|Ten brëkòwnik mô sã za '''[[Hùmanistika|humanistã]]'''.|Ta brëkòwniczka mô sã za '''[[Hùmanistika|humanistkã]]'''.}}|tło1=white|tło2=#D8BFD8|ramka=black}}{{Brëkòkôstka|[[Òbrôzk:Palestine Waving Flag Icon.svg|48px|Palestine Waving Flag Icon]]|{{gender:{{BASEPAGENAME}}|Ten wikipedista|Ta wikipedistka}} <b>pòpiérô</b> biôtkã palestińsczégò nôrodu ë sprocëmniwô sã lëdobójstwù w Conie Gazë|tło1=#009000|tło2=#008000|kolor2=white}}{{Brëkòkôstka|[[Òbrôzk:Bicycle-icon.png]]|{{gender:{{BASEPAGENAME}}|Ten brëkòwnik|Ta brëkòwniczka}} rôd [[rekreacjô|rekreacjowò]] jezdzy na [[Kòło|kòle]].|tło1=white|tło2=lightgrey}}{{Brëkòkôstka|tło1=#FEAAAA|tło2=white|[[Òbrôzk:Anticapitalismo.svg|45px]]|Ten brëkòwnik jest procëmny wprowadzeniu [[eùro]]. Je òn dbë, że mòże pò wprowadzeniu ti walutë swiat sã nie zawôli, le równak Polskô stracy cësk na mònetarną pòlitikã w gwôsnym kraju.}}{{Brëkòkôstka|{{NUMBEROFARTICLES}}|Nen brëkòwnik mëszli, że kaszëbskô Wikipediô mô za mało artiklów.|tło1=#7E7CF2|tło2=#4CEB3F}}{{Brëkòkôstka
| ramka = Black
| tło2 = #CC0000
| tło1 = White
| farwa2 = White
| 1 = [[Òbrôzk:Grammar Nazi Icon Text.svg|80px]]
| 2 = Ten brëkòwnik je '''òrtograficznym''' faszistą.
}}
jiju9327e685i74cx2tzw84870lk4ev
Mésterstwò Swiata w Fùsbalë 2026
0
13158
204447
204434
2026-07-05T14:10:50Z
HaxelKaszëba
17492
ùprawkô
204447
wikitext
text/x-wiki
{{Spòrtowi Turniér infobox|gòspòdôrzë=[[Òbrôzk:Flag of Canada.svg|tło|22x22px]] [[Kanada]]
[[Òbrôzk:Flag of Mexico.svg|tło|22x22px]] [[Meksyk]]
[[Òbrôzk:Flag of the United States.svg|tło|22x22px]] [[Zjednóné Kraje Americzi|Zjednóné Krôje Americzi]]|data=11 czerwińca 2026|pòzwa=Mésterstwò Swiata w fùsbalë 2026<br>FIFA World Cup 2026<br>Coupe du monde de football de 2026<br>Copa Mundial de Fútbol 2026|òbrôzk=2026 FIFA World Cup emblem (with wordmark).svg|reprezentacje=48|stadiónë=16|mecze=90|gòle= 261|ùszłô=[[Mésterstwò Swiata w fùsbalë 2022|2022]]|aktualné=2026]}}
'''XXIII Mésterstwò Swiata w fùsbalë 2026''' – 23. edicjô mésterstwô swiata twòrzoné przez [[FIFA]]. Turniér òdbëwô sã òd [[11 czerwińca]] do [[19 lëpińca]] 2026 rokù na stadionach w [[Kanada|Kanadze]], [[Meksyk|Meksykù]] a [[Zjednóné Kraje Americzi|Zjednónëch Krôjach]]<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026 FIFA World Cup 2026 Official Website]''. [przistãp 2026-06-29].</ref>. Je to pierszi mùndial twòrzony wespólnie przez trzë krôje òrôz pierszi w historie z dzélã 48 nôrodnëch reprezentacjów.
Nowi fòrmat turniéru òbjimùje 12 grëp pò szterëch karnów, z jaczich do pùcharowi fazë awansëje dwie nôlepszé reprezentacje z kòżdi grëpë òrôz òsmë nôlepszich karnów z trzecëch placów. Òznôczô to rozegranié rekordowi lëczbë 104 zeńdzeniów<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/fifa-council-approves-international-match-calendar-new-format FIFA Council approves international match calendar and 104-match World Cup format]''. [przistãp 2026-06-29].</ref>.
== Welënk gòspòdôrzów ==
[[Òbrôzk:2026 world cup.svg|mały|Wëniczi w òsoblëwëch krôjach]]
13 czerwińca 2018 rokù òb czas Kòngresu FIFA w Mòskwie dobéła wespólna kandidatura Kanadë, Meksykù a Zjednónëch Krôjów, przedobiwôjącë bédënk [[Marokò|Maroka]]<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/united-2026-bid-wins-hosts-fifa-world-cup United 2026 bid voted hosts of the 2026 FIFA World Cup]''. [przistãp 2026-06-29].</ref>.
Turniér je historiczny z czilu pòwodów:
* pierszi mundial twòrzony przez trzë krôje;
* Meksyk òstôł pierszim krôjã, jaczi gòscy mésterstwò swiata pò rôz trzecy;
* Kanada pò rôz pierszi twòrzy chłopsczi mésterstwò swiata;
* Zjednóné Krôje twòrzą mundial pò rôz drëdżi (pò 1994 rokù).
Hewò wëniczi welowaniô:
{| class="wikitable" style="text-align: center;"
!Państwò
!Głosë
|-
| align="left" |[[Òbrôzk:Flag of Canada.svg|tło|22x22px]] [[Kanada]]
[[Òbrôzk:Flag of Mexico.svg|tło|22x22px]] [[Meksyk]]
[[Òbrôzk:Flag of the United States.svg|tło|22x22px]]
[[Zjednóné Kraje Americzi|Zjednóné Krôje Americzi]]
|'''134'''
|-
| align="left" | [[Òbrôzk:Flag of Morocco.svg|tło|22x22px]] [[Marokò]]
|65
|}
== Fòrmat rozgriwków ==
Pò rôz pierszi w historie w finałach wëstãpùje 48 reprezentacjów.
[[Òbrôzk:FIFA World Cup 2026 Draw Reception (54972845582).jpg|mały|Òficjalnô bala na mésterstwô, Trionda]]
Fòrmat òbjimùje:
* 12 grëp pò 4 karnów,
* Kòżdé karno rozgriwô 3 grëpowé mecze,
* awans zwëskiwają:
** dobiwcë grëp,
** karnë z drëdżich placów,
** òsmë nôlepszich reprezentacjów z trzecych placów,
* òd pùcharowi fazë òbrzeszëwùje pùcharowi system rozpòczinôjącë sã òd 1/16 finału (Round of 32).
== Gardë gòspòdôrzów ==
Turniér òdbëwô sã na 16 stadionach<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/fifa-unveils-stellar-line-up-of-host-cities-for-2026-world-cup FIFA unveils stellar line-up of host cities for 2026 FIFA World Cup]''. [przistãp 2026-06-29].</ref>.
=== [[Kanada]] ===
* Toronto
* Vancouver
=== [[Meksyk]] ===
* Meksyk
* Guadalajara
* Monterrey
=== [[Zjednóné Kraje Americzi|Zjednóné Krôje]] ===
* Atlanta
* Boston
* Dallas
* Houston
* Kansas City
* Los Angeles
* Miami
* [[Nowi Jork]] / East Rutherford
* Filadelfia
* San Francisco Bay Area (Santa Clara)
* Seattle
== Grëpòwa faza ==
Reprezentacje òstôłë pòdzelóné na 12 grëp òznôczonëch lëterama òd A do L. Kòżdé karno rozgriwô trzë zeńdzeniô.
=== Grëpa A ===
{{#invoke:Grëpa MS|render
|group=Grëpa A
|1=[[File:Flag of Mexico.svg|20px]] Meksyk
|1pkt=9
|1m=3
|1w=3
|1r=0
|1p=0
|1g+=6
|1g-=0
|1rg=+6
|2=[[File:Flag of South Africa.svg|20px]] Pôłniowô Afrika
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=2
|2g-=3
|2rg=-1
|3=[[File:Flag of South Korea.svg|20px]] Pôłniowô Korea
|3pkt=3
|3m=3
|3w=1
|3r=0
|3p=2
|3g+=2
|3g-=3
|3rg=-1
|4=[[File:Flag of Czech Republic.svg|20px]] Czeskô
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=2
|4g-=4
|4rg=-2
}}
=== Grëpa B ===
{{#invoke:Grëpa MS|render
|group=Grëpa B
|advance=3
|1=[[File:Flag of Switzerland.svg|20px]] Szwajcarskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=6
|1g-=3
|1rg=+3
|2=[[File:Flag of Canada.svg|20px]] Kanada
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=5
|2g-=4
|2rg=+1
|3=[[File:Flag of Bosnia and Herzegovina.svg|20px]] Bòsniô a Hercegòwina
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=4
|3g-=5
|3rg=-1
|4=[[File:Flag of Qatar.svg|20px]] Katar
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=2
|4g-=9
|4rg=-7
}}
=== Grëpa C ===
{{#invoke:Grëpa MS|render
|group=Grëpa C
|1=[[File:Flag of Brazil.svg|20px]] Brazylskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=7
|1g-=1
|1rg=+6
|2=[[File:Flag of Morocco.svg|20px]] Marokò
|2pkt=7
|2m=3
|2w=2
|2r=1
|2p=0
|2g+=6
|2g-=3
|2rg=+3
|3=[[File:Flag of Scotland.svg|20px]] Szkòckô
|3pkt=3
|3m=3
|3w=1
|3r=0
|3p=2
|3g+=2
|3g-=5
|3rg=-3
|4=[[File:Flag of Haiti.svg|20px]] Haiti
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=2
|4g-=8
|4rg=-6
}}
=== Grëpa D ===
{{#invoke:Grëpa MS|render
|group=Grëpa D
|advance=3
|1=[[File:Flag of United States.svg|20px]] USA
|1pkt=6
|1m=3
|1w=2
|1r=0
|1p=1
|1g+=8
|1g-=4
|1rg=+4
|2=[[File:Flag of Australia.svg|20px]] Aùstraliô
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=2
|2g-=2
|2rg=0
|3=[[File:Flag of Paraguay.svg|20px]] Paragwaj
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=3
|3g-=4
|3rg=-1
|4=[[File:Flag of Turkey.svg|20px]] Tëreckô
|4pkt=3
|4m=3
|4w=1
|4r=0
|4p=2
|4g+=3
|4g-=5
|4rg=-2
}}
=== Grëpa E ===
{{#invoke:Grëpa MS|render
|group=Grëpa E
|advance=3
|1=[[File:Flag of Germany.svg|20px]] Niemieckô
|1pkt=6
|1m=3
|1w=2
|1r=0
|1p=1
|1g+=10
|1g-=4
|1rg=+6
|2=[[File:Flag of Ivory Coast.svg|20px]] Ùbrzég Słoniowëch Kłów
|2pkt=6
|2m=3
|2w=2
|2r=0
|2p=1
|2g+=4
|2g-=2
|2rg=+2
|3=[[File:Flag of Ecuador.svg|20px]] Ekwador
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=2
|3g-=2
|3rg=0
|4=[[File:Flag of Curaçao.svg|20px]] Curaçao
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=1
|4g-=9
|4rg=-8
}}
=== Grëpa F ===
{{#invoke:Grëpa MS|render
|group=Grëpa F
|advance=3
|1=[[File:Flag of Netherlands.svg|20px]] Niderlandzkô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=10
|1g-=4
|1rg=+6
|2=[[File:Flag of Japan.svg|20px]] Japòńskô
|2pkt=5
|2m=3
|2w=1
|2r=2
|2p=0
|2g+=7
|2g-=3
|2rg=+4
|3=[[File:Flag of Sweden.svg|20px]] Szwedzkô
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=7
|3g-=7
|3rg=0
|4=[[File:Flag of Tunisia.svg|20px]] Tunezjô
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=2
|4g-=12
|4rg=-10
}}
=== Grëpa G ===
{{#invoke:Grëpa MS|render
|group=Grëpa G
|1=[[File:Flag of Belgium.svg|20px]] Belgiô
|1pkt=5
|1m=3
|1w=1
|1r=2
|1p=0
|1g+=6
|1g-=2
|1rg=+4
|2=[[File:Flag of Egypt.svg|20px]] Egipt
|2pkt=5
|2m=3
|2w=1
|2r=2
|2p=0
|2g+=5
|2g-=3
|2rg=+2
|3=[[File:Flag of Iran.svg|20px]] Iran
|3pkt=3
|3m=3
|3w=0
|3r=3
|3p=0
|3g+=3
|3g-=3
|3rg=0
|4=[[File:Flag of New Zealand.svg|20px]] Nowô Zelandzkô
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=4
|4g-=10
|4rg=-6
}}
=== Grëpa H ===
{{#invoke:Grëpa MS|render
|group=Grëpa H
|1=[[File:Flag of Spain.svg|20px]] Szpańskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=5
|1g-=0
|1rg=+5
|2=[[File:Flag of Cape Verde.svg|20px]] Cabo Verde
|2pkt=3
|2m=3
|2w=0
|2r=3
|2p=0
|2g+=2
|2g-=2
|2rg=0
|3=[[File:Flag of Uruguay.svg|20px]] Ùrugwaj
|3pkt=2
|3m=3
|3w=0
|3r=2
|3p=1
|3g+=3
|3g-=4
|3rg=-1
|4=[[File:Flag of Saudi Arabia.svg|20px]] Saùdijskô Arabiô
|4pkt=2
|4m=3
|4w=0
|4r=2
|4p=1
|4g+=1
|4g-=5
|4rg=-4
}}
=== Grëpa I ===
{{#invoke:Grëpa MS|render
|group=Grëpa I
|advance=3
|1=[[File:Flag of France.svg|20px]] Francëskô
|1pkt=9
|1m=3
|1w=3
|1r=0
|1p=0
|1g+=10
|1g-=2
|1rg=+8
|2=[[File:Flag of Norway.svg|20px]] Norweskô
|2pkt=6
|2m=3
|2w=2
|2r=0
|2p=1
|2g+=8
|2g-=7
|2rg=+1
|3=[[File:Flag of Senegal.svg|20px]] Senegal
|3pkt=3
|3m=3
|3w=1
|3r=0
|3p=2
|3g+=8
|3g-=6
|3rg=+2
|4=[[File:Flag of Iraq.svg|20px]] Irak
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=1
|4g-=12
|4rg=-11
}}
=== Grëpa J ===
{{#invoke:Grëpa MS|render
|group=Grëpa J
|advance=3
|1=[[File:Flag of Argentina.svg|20px]] Argentińskô
|1pkt=9
|1m=3
|1w=3
|1r=0
|1p=0
|1g+=8
|1g-=1
|1rg=+7
|2=[[File:Flag of Austria.svg|20px]] Aùstriackô
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=7
|2g-=7
|2rg=0
|3=[[File:Flag of Algeria.svg|20px]] Algierskô
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=5
|3g-=7
|3rg=-2
|4=[[File:Flag of Jordan.svg|20px]] Jordańskô
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=3
|4g-=8
|4rg=-5
}}
=== Grëpa K ===
{{#invoke:Grëpa MS|render
|group=Grëpa K
|advance=3
|1=[[File:Flag of Colombia.svg|20px]] Kòlumbiô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=4
|1g-=1
|1rg=+3
|2=[[File:Flag of Portugal.svg|20px]] Pòrtugalskô
|2pkt=5
|2m=3
|2w=1
|2r=2
|2p=0
|2g+=6
|2g-=1
|2rg=+5
|3=[[File:Flag of Democratic Republic of the Congo.svg|20px]] DRK
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=4
|3g-=3
|3rg=+1
|4=[[File:Flag of Uzbekistan.svg|20px]] Ùzbekistan
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=2
|4g-=11
|4rg=-9
}}
=== Grëpa L ===
{{#invoke:Grëpa MS|render
|group=Grëpa L
|advance=3
|1=[[File:Flag of England.svg|20px]] Anielskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=6
|1g-=2
|1rg=+4
|2=[[File:Flag of Croatia.svg|20px]] Chòrwackô
|2pkt=6
|2m=3
|2w=2
|2r=0
|2p=1
|2g+=5
|2g-=5
|2rg=0
|3=[[File:Flag of Ghana.svg|20px]] Ghana
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=2
|3g-=2
|3rg=0
|4=[[File:Flag of Panama.svg|20px]] Panama
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=0
|4g-=4
|4rg=-4
}}
=== Ranking karnów z trzecëch placów ===
{| class="wikitable" style="width:100%; table-layout:fixed; text-align:center; font-family:system-ui, Arial;"
<tr style="background:#f8f9fa;">
<th style="width:6%;">M.</th>
<th style="width:40%;">Reprezentacjô</th>
<th style="width:15%;">Grëpa</th>
<th style="width:9%;">Pkt</th>
<th style="width:6%;">M</th>
<th style="width:6%;">W</th>
<th style="width:6%;">R</th>
<th style="width:6%;">P</th>
<th style="width:6%;">+</th>
<th style="width:6%;">–</th>
<th style="width:6%;">±</th>
</tr>
|-
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Democratic Republic of the Congo.svg|20px]] DRK
| style="background:#dff6df; font-weight:bold;" | K
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | +1
|-
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Ecuador.svg|20px]] Ekwador
| style="background:#dff6df; font-weight:bold;" | E
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 0
|-
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Sweden.svg|20px]] Szwedzkô
| style="background:#dff6df; font-weight:bold;" | F
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold;" | 0
|-
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Bosnia and Herzegovina.svg|20px]] Bòsniô a Hercegòwina
| style="background:#dff6df; font-weight:bold;" | B
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 5
| style="background:#dff6df; font-weight:bold;" | −1
|-
| style="background:#dff6df; font-weight:bold;" | 5
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Paraguay.svg|20px]] Paragwaj
| style="background:#dff6df; font-weight:bold;" | D
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | −1
|-
| style="background:#dff6df; font-weight:bold;" | 6
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Ghana.svg|20px]] Ghana
| style="background:#dff6df; font-weight:bold;" | L
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 0
|-
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Algeria.svg|20px]] Algierskô
| style="background:#dff6df; font-weight:bold;" | J
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 5
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold;" | −2
|-
| style="background:#dff6df; font-weight:bold;" | 8
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Senegal.svg|20px]] Senegal
| style="background:#dff6df; font-weight:bold;" | I
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 0
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 8
| style="background:#dff6df; font-weight:bold;" | 6
| style="background:#dff6df; font-weight:bold;" | +2
|-
| 9
| style="text-align:left;" | [[File:Flag of Iran.svg|20px]] Iran
| G
| 3 || 3 || 0 || 3 || 0 || 3 || 3 || 0
|-
| 10
| style="text-align:left;" | [[File:Flag of South Korea.svg|20px]] Pôłniowô Korea
| A
| 3 || 3 || 1 || 0 || 2 || 2 || 3 || −1
|-
| 11
| style="text-align:left;" | [[File:Flag of Scotland.svg|20px]] Szkòckô
| C
| 3 || 3 || 1 || 0 || 2 || 2 || 5 || −3
|-
| 12
| style="text-align:left;" | [[File:Flag of Uruguay.svg|20px]] Ùrugwaj
| H
| 2 || 3 || 0 || 2 || 1 || 3 || 4 || −1
|}
== Pùcharowô faza ==
=== Drôbka ===
{{Turniérowô drôbka
| boldwinner=y
| RD1= 1/16 finału
| RD2= 1/8 finału
| RD1-text01=29 czerwińca – [[Gillette Stadium|Foxborough]]
| RD1-team01=[[File:Flag of Germany.svg|20px]] Niemieckô
| RD1-score01=1 (3)
| RD1-team02=[[File:Flag of Paraguay.svg|20px]] Paragwaj
| RD1-score02=1 (4)
| RD1-text03=28 czerwińca – [[SoFi Stadium|Inglewood]]
| RD1-team03=[[File:Flag of France.svg|20px]] Francëskô
| RD1-score03=3
| RD1-team04=[[File:Flag of Sweden.svg|20px]] Szwedzkô
| RD1-score04=0
| RD1-text05=3 lëpińca – [[BMO Field|Toronto]]
| RD1-team05=[[File:Flag of South Africa.svg|20px]] Pôłniowô Afrika
| RD1-team06=[[File:Flag of Canada.svg|20px]] Kanada
| RD1-score05=0
| RD1-score06=1
| RD1-text07=2 lëpińca – [[Levi’s Stadium|Santa Clara]]
| RD1-team07=[[File:Flag of Netherlands.svg|20px]] Niderlandzkô
| RD1-team08=[[File:Flag of Morocco.svg|20px]] Marokò
| RD1-score07=1 (2)
| RD1-score08=1 (3)
| RD1-text09=29 czerwińca – [[NRG Stadium|Houston]]
| RD1-team09=[[File:Flag of Portugal.svg|20px]] Pòrtugalskô
| RD1-team10=[[File:Flag of Croatia.svg|20px]] Chòrwackô
| RD1-score09=2
| RD1-score10=1
| RD1-text11=1 lëpińca – [[Estadio Azteca|Meksyk]]
| RD1-team11=[[File:Flag of Spain.svg|20px]] Szpańskô
| RD1-team12=[[File:Flag of Austria.svg|20px]] Aùstriackô
| RD1-score11=3
| RD1-score12=0
| RD1-text13=4 lëpińca – [[Hard Rock Stadium|Miami Gardens]]
| RD1-team13=[[File:Flag of United States.svg|20px]] USA
| RD1-team14=[[File:Flag of Bosnia and Herzegovina.svg|20px]] Bòsniô a Hercegòwina
| RD1-score13=2
| RD1-score14=0
| RD1-text15=3 lëpińca – [[BC Place Stadium|Vancouver]]
| RD1-team15=[[File:Flag of Belgium.svg|20px]] Belgiô
| RD1-team16=[[File:Flag of Senegal.svg|20px]] Senegal
| RD1-score15=3
| RD1-score16=2
| RD1-text17=
| RD1-team17=[[File:Flag of Brazil.svg|20px]] Brazylskô
| RD1-team18=[[File:Flag of Japan.svg|20px]] Japòńskô
| RD1-score17=2
| RD1-score18=1
| RD1-text19=
| RD1-team19=[[File:Flag of Ivory Coast.svg|20px]] Ùbrzég Słoniowëch Kłów
| RD1-team20=[[File:Flag of Norway.svg|20px]] Norweskô
| RD1-score19=1
| RD1-score20=2
| RD1-text21=
| RD1-team21=[[File:Flag of Mexico.svg|20px]] Meksyk
| RD1-team22=[[File:Flag of Ecuador.svg|20px]] Ekwador
| RD1-score21=2
| RD1-score22=0
| RD1-text23=
| RD1-team23=[[File:Flag of England.svg|20px]] Anielskô
| RD1-team24=[[File:Flag of the Democratic Republic of the Congo.svg|20px]] DRK
| RD1-score23=2
| RD1-score24=1
| RD1-text25=
| RD1-team25=[[File:Flag of Argentina.svg|20px]] Argentińskô
| RD1-team26=[[File:Flag of Cape Verde.svg|20px]] Cabo Verde
| RD1-score25=3
| RD1-score26=2
| RD1-text27=
| RD1-team27=[[File:Flag of Australia.svg|20px]] Aùstraliô
| RD1-team28=[[File:Flag of Egypt.svg|20px]] Egipt
| RD1-score27=1 (2)
| RD1-score28=1 (4)
| RD1-text29=
| RD1-team29=[[File:Flag of Switzerland.svg|20px]] Szwajcarskô
| RD1-team30=[[File:Flag of Algeria.svg|20px]] Algierskô
| RD1-score29=2
| RD1-score30=0
| RD1-text31=
| RD1-team31=[[File:Flag of Colombia.svg|20px]] Kòlumbiô
| RD1-team32=[[File:Flag of Ghana.svg|20px]] Ghana
| RD1-score31=1
| RD1-score32=0
| RD2-text01=4 lëpińca – [[Lincoln Financial Field|Filadelfia]]
| RD2-team01=[[File:Flag of Paraguay.svg|20px]] Paragwaj
| RD2-team02=[[File:Flag of France.svg|20px]] Francëskô
| RD2-score01=0
| RD2-score02=1
| RD2-text02=4 lëpińca – [[NRG Stadium|Houston]]
| RD2-team03=[[File:Flag of Canada.svg|20px]] Kanada
| RD2-team04=[[File:Flag of Morocco.svg|20px]] Marokò
| RD2-score03=0
| RD2-score04=3
| RD2-text03=6 lëpińca – [[AT&T Stadium|Arlington]]
| RD2-team05=[[File:Flag of Portugal.svg|20px]] Pòrtugalskô
| RD2-team06=[[File:Flag of Spain.svg|20px]] Szpańskô
| RD2-score05=
| RD2-score06=
| RD2-text04=7 lëpińca – [[CenturyLink Field|Seattle]]
| RD2-team07=[[File:Flag of USA.svg|20px]] USA
| RD2-team08=[[File:Flag of Belgium.svg|20px]] Belgiô
| RD2-score07=
| RD2-score08=
| RD2-text05=5 lëpińca – [[MetLife Stadium|East Rutherford]]
| RD2-team09=[[File:Flag of Brazil.svg|20px]] Brazylskô
| RD2-team10=[[File:Flag of Norway.svg|20px]] Norweskô
| RD2-score09=
| RD2-score10=
| RD2-text06=6 lëpińca – [[Estadio Azteca|Meksyk]]
| RD2-team11=[[File:Flag of Mexico.svg|20px]] Meksyk
| RD2-team12=[[File:Flag of England.svg|20px]] Anielskô
| RD2-score11=
| RD2-score12=
| RD2-text07=7 lëpińca – [[Mercedes-Benz Stadium|Atlanta]]
| RD2-team13=[[File:Flag of Argentina.svg|20px]] Argentińskô
| RD2-team14=[[File:Flag of Egypt.svg|20px]] Egipt
| RD2-score13=
| RD2-score14=
| RD2-text08=7 lëpińca – [[BC Place Stadium|Vancouver]]
| RD2-team15=[[File:Flag of Switzerland.svg|20px]] Szwajcarskô
| RD2-team16=[[File:Flag of Colombia.svg|20px]] Kòlumbiô
| RD2-score15=
| RD2-score16=
| RD3-text01=9 lëpińca – [[Gillette Stadium|Foxborough]]
| RD3-team01=[[File:Flag of Morocco.svg|20px]] Marokò
| RD3-team02=[[File:Flag of France.svg|20px]] Francëskô
| RD3-score01=
| RD3-score02=
| RD3-text02=10 lëpińca – [[SoFi Stadium|Inglewood]]
| RD3-team03=Dobiwca meczu 93
| RD3-team04=Dobiwca meczu 94
| RD3-score03=
| RD3-score04=
| RD3-text03=11 lëpińca – [[Hard Rock Stadium|Miami Gardens]]
| RD3-team05=Dobiwca meczu 91
| RD3-team06=Dobiwca meczu 92
| RD3-score05=
| RD3-score06=
| RD3-text04=12 lëpińca – [[Arrowhead Stadium|Kansas City]]
| RD3-team07=Dobiwca meczu 95
| RD3-team08=Dobiwca meczu 96
| RD3-score07=
| RD3-score08=
| RD4-text01=14 lëpińca – [[AT&T Stadium|Arlington]]
| RD4-team01=Dobiwca meczu 97
| RD4-team02=Dobiwca meczu 98
| RD4-score01=
| RD4-score02=
| RD4-text02=15 lëpińca – [[Mercedes-Benz Stadium|Atlanta]]
| RD4-team03=Dobiwca meczu 99
| RD4-team04=Dobiwca meczu 100
| RD4-score03=
| RD4-score04=
| RD5-text01=19 lëpińca – [[MetLife Stadium|East Rutherford]]
| RD5-team01=Dobiwca meczu 101
| RD5-team02=Dobiwca meczu 102
| RD5-score01=
| RD5-score02=
| RD5b-text01=18 lëpińca – [[Hard Rock Stadium|Miami Gardens]]
| RD5b-team01=Przerżnóny meczu 101
| RD5b-team02=Przerżnóny meczu 102
| RD5b-score01=
| RD5b-score02=
}}
=== 1/16 finału ===
{{fùsbalowi mecz
| etap = Mecz 73
| data = 28 czerwińca 2026, 21:00
| stadión = SoFi Stadium, Los Angeles
| druzyna1 = Pôłniowô Afrika
| fana1 = RSA
| wënik1 = 0
| druzyna2 = Kanada
| fana2 = Canada
| wënik2 = 1
| gòle2 = Eustáquio 90+2
| òbzérôczë = 69 237
| sãdza = João Pinheiro
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 74
| data = 29 czerwińca 2026, 19:00
| stadión = NRG Stadium, Houston
| druzyna1 = Brazylskô
| fana1 = Brazil
| wënik1 = 2
| druzyna2 = Japòńskô
| fana2 = Japan
| wënik2 = 1
| gòle1 = Casemiro 56’, Martinelli 90+5
| gòle2 = Sano 29’
| òbzérôczë = 68 777
| sãdza = Maurizio Mariani
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 75
| data = 29 czerwińca 2026, 22:30
| stadión = Gillette Stadium, Boston
| druzyna1 = Niemieckô
| fana1 = Germany
| wënik1 = 1
| druzyna2 = Paragwaj
| fana2 = Paraguay
| wënik2 = 1
| karné = 3-4
| gòle1 = Havertz 54’
| gòle2 = Enciso 42’
| òbzérôczë = 63 945
| sãdza = Jalal Jayed
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 76
| data = 30 czerwińca 2026, 03:00
| stadión = Estadio BBVA, Monterrey
| druzyna1 = Niderlandzkô
| fana1 = Netherlands
| wënik1 = 1
| gòle1 = Gakpo 72’
| druzyna2 = Marokò
| fana2 = Morocco
| wënik2 = 1
| karné = 2-3
| gòle2 = Diop 90+1
| òbzérôczë = 68 777
| sãdza = Wilton Sampaio
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 77
| data = 30 czerwińca 2026, 19:00
| stadión = AT&T Stadium, Arlington
| druzyna1 = Ùbrzég Słoniowëch Kłów
| fana1 = Ivory Coast
| wënik1 = 1
| gòle1 = Diallo 74’
| druzyna2 = Norweskô
| fana2 = Norway
| wënik2 = 2
| gòle2 = Nusa 39’, Haaland 86’
| òbzérôczë = 69 665
| sãdza = Jesús Valenzuela
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 78
| data = 30 czerwińca 2026, 23:00
| stadión = MetLife Stadium, East Rutherford
| druzyna1 = Francëskô
| fana1 = France
| wënik1 = 3
| druzyna2 = Szwedzkô
| fana2 = Sweden
| wënik2 = 0
| gòle1 = Mbappé 45’, 74’; Barcola 53’
| òbzérôczë = 80 663
| sãdza = Danny Makkelie
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 79
| data = 1 lëpińca 2026, 03:00
| stadión = Estadio Azteca, Meksyk
| druzyna1 = Meksyk
| fana1 = Mexico
| wënik1 = 2
| druzyna2 = Ekwador
| fana2 = Ecuador
| wënik2 = 0
| gòle1 = Quiñones 22’, Jiménez 31’
| òbzérôczë = 80 824
| sãdza = Slavko Vinčić
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 80
| data = 1 lëpińca 2026, 18:00
| stadión = Mercedes-Benz Stadium, Atlanta
| druzyna1 = Anielskô
| fana1 = England
| wënik1 = 2
| druzyna2 = Demòkraticznô Repùblika Kònga
| fana2 = Democratic Republic of the Congo
| wënik2 = 1
| gòle1 = Kane 75’, 86’
| gòle2 = Cipenga 7’
| òbzérôczë = 68 239
| sãdza = Adham Makhadmeh
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 81
| data = 1 lëpińca 2026, 22:00
| stadión = Lumen Field, Seattle
| druzyna1 = Belgiô
| fana1 = Belgium
| wënik1 = 3
| gòle1 = Lukaku 86’; Tielemans 89’, 120+5'
| druzyna2 = Senegal
| fana2 = Senegal
| wënik2 = 2
| gòle2 = Diarra 24’, Sarr 51’
| òbzérôczë = 66 925
| sãdza = Said Martínez
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 82
| data = 2 lëpińca 2026, 02:00
| stadión = Levi’s Stadium, Santa Clara
| druzyna1 = USA
| fana1 = USA
| wënik1 = 2
| gòle1 = Balogun 78’, Tillman 82'
| druzyna2 = Bòsniô a Hercegòwina
| fana2 = Bosnia and Herzegovina
| wënik2 = 0
| òbzérôczë = 68 827
| sãdza = Raphael Claus
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 83
| data = 2 lëpińca 2026, 22:00
| stadión = SoFi Stadium, Los Angeles
| druzyna1 = Szpańskô
| fana1 = Spain
| wënik1 = 3
| gòle1 = Oyarzabal 36', 89'; Porro 66'
| druzyna2 = Aùstriackô
| fana2 = Austria
| wënik2 = 0
| òbzérôczë = 70 492
| sãdza = Glenn Nyberg
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 84
| data = 2 lëpińca 2026, 18:00
| stadión = GEHA Field at Arrowhead Stadium, Kansas City
| druzyna1 = Pòrtugalskô
| fana1 = Portugal
| wënik1 = 2
| gòle1 = Cristiano Ronaldo 68' (k.), Gonçalo Ramos 90+4'
| druzyna2 = Chòrwackô
| fana2 = Croatia
| wënik2 = 1
| gòle2 = Perišić 41'
| òbzérôczë = 43 036
| sãdza = Espen Eskas
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 85
| data = 3 lëpińca 2026, 02:00
| stadión = BC Place, Vancouver
| druzyna1 = Szwajcarskô
| fana1 = Switzerland
| wënik1 = 2
| gòle1 = Embolo 10'; Ndoye 46'
| druzyna2 = Algerskô
| fana2 = Algeria
| wënik2 = 0
| òbzérôczë = 52 497
| sãdza = Yael Falcon Perez
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 86
| data = 3 lëpińca 2026, 20:00
| stadión = AT&T Stadium, Arlington
| druzyna1 = Aùstraliô
| fana1 = Australia
| wënik1 = 1
| druzyna2 = Egipt
| fana2 = Egypt
| wënik2 = 1
| karné = 2-4
| gòle1 = Hany 55'
| gòle2 = Ashour 13'
| òbzérôczë = 70 244
| sãdza = Gustavo Tejera
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 87
| data = 3 lëpińca 2026, 22:00
| stadión = Hard Rock Stadium, Miami Gardens
| druzyna1 = Argentińskô
| fana1 = Argentina
| wënik1 = 3
| gòle1 = Messi 29'; Lautaro Martínez 92'; Diney 111'
| druzyna2 = Cabo Verde
| fana2 = Cape Verde
| wënik2 = 2
| gòle2 = D. Duarte 59'; Lopes Cabral 103'
| òbzérôczë = 64 478
| sãdza = Drew Fischer
}}
<br>
-------
{{fùsbalowi mecz
| etap = Mecz 88
| data = 4 lëpińca 2026, 02:30
| stadión = GEHA Field at Arrowhead Stadium, Kansas City
| druzyna1 = Kòlumbiô
| fana1 = Colombia
| wënik1 = 1
| gòle1 = J. Arias 14'
| druzyna2 = Ghana
| fana2 = Ghana
| wënik2 = 0
| òbzérôczë = 69 045
| sãdza = Clément Turpin
}}
=== 1/8 finału ===
{{fùsbalowi mecz
| etap = Mecz 89
| data = 4 lëpińca 2026, 13:00
| stadión = NRG Stadium, Houston
| druzyna1 = Kanada
| fana1 = Canada
| wënik1 = 0
| druzyna2 = Marokò
| fana2 = Morocco
| wënik2 = 3
| gòle2 = Ounahi 50’, 82’; Rahimi 90+8’
| òbzérôczë = 68 777
| sãdza = Michael Oliver
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 90
| data = 4 lëpińca 2026, 21:00
| stadión = MetLife Stadium, East Rutherford
| druzyna1 = Paragwaj
| fana1 = Paraguay
| wënik1 = 0
| druzyna2 = Francëskô
| fana2 = France
| wënik2 = 1
| gòle2 = Mbappé 70' (k.)
| òbzérôczë = 68 324
| sãdza = Ilgiz Tantashev
}}
<br>
'''Bôczënk:''' jinfòrmacje zrzeszoné z pòstãpnyma zeńdzeniama mdą aktualizowóné przez całi czas.
== Finał ==
Finał òdbëwa sã [[19 lëpińca]] 2026 rokù na stadionie ''MetLife Stadium'' w East Rutherford (aglomeracjô [[Nowi Jork|Nowégò Jorkù]]).
== Òbaczë téż ==
{{#categorytree:Nożnô bala|hideroot=on|mode=pages}}
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
{{Kòntrola}}
[[Kategòrëjô:Nożnô bala]]
rdlva3g4q2tobr9v215istt08xo45m1
204490
204447
2026-07-05T22:24:52Z
HaxelKaszëba
17492
norweskô dobéła
204490
wikitext
text/x-wiki
{{Spòrtowi Turniér infobox|gòspòdôrzë=[[Òbrôzk:Flag of Canada.svg|tło|22x22px]] [[Kanada]]
[[Òbrôzk:Flag of Mexico.svg|tło|22x22px]] [[Meksyk]]
[[Òbrôzk:Flag of the United States.svg|tło|22x22px]] [[Zjednóné Kraje Americzi|Zjednóné Krôje Americzi]]|data=11 czerwińca 2026|pòzwa=Mésterstwò Swiata w fùsbalë 2026<br>FIFA World Cup 2026<br>Coupe du monde de football de 2026<br>Copa Mundial de Fútbol 2026|òbrôzk=2026 FIFA World Cup emblem (with wordmark).svg|reprezentacje=48|stadiónë=16|mecze=91|gòle= 264|ùszłô=[[Mésterstwò Swiata w fùsbalë 2022|2022]]|aktualné=2026]}}
'''XXIII Mésterstwò Swiata w fùsbalë 2026''' – 23. edicjô mésterstwô swiata twòrzoné przez [[FIFA]]. Turniér òdbëwô sã òd [[11 czerwińca]] do [[19 lëpińca]] 2026 rokù na stadionach w [[Kanada|Kanadze]], [[Meksyk|Meksykù]] a [[Zjednóné Kraje Americzi|Zjednónëch Krôjach]]<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026 FIFA World Cup 2026 Official Website]''. [przistãp 2026-06-29].</ref>. Je to pierszi mùndial twòrzony wespólnie przez trzë krôje òrôz pierszi w historie z dzélã 48 nôrodnëch reprezentacjów.
Nowi fòrmat turniéru òbjimùje 12 grëp pò szterëch karnów, z jaczich do pùcharowi fazë awansëje dwie nôlepszé reprezentacje z kòżdi grëpë òrôz òsmë nôlepszich karnów z trzecëch placów. Òznôczô to rozegranié rekordowi lëczbë 104 zeńdzeniów<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/fifa-council-approves-international-match-calendar-new-format FIFA Council approves international match calendar and 104-match World Cup format]''. [przistãp 2026-06-29].</ref>.
== Welënk gòspòdôrzów ==
[[Òbrôzk:2026 world cup.svg|mały|Wëniczi w òsoblëwëch krôjach]]
13 czerwińca 2018 rokù òb czas Kòngresu FIFA w Mòskwie dobéła wespólna kandidatura Kanadë, Meksykù a Zjednónëch Krôjów, przedobiwôjącë bédënk [[Marokò|Maroka]]<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/united-2026-bid-wins-hosts-fifa-world-cup United 2026 bid voted hosts of the 2026 FIFA World Cup]''. [przistãp 2026-06-29].</ref>.
Turniér je historiczny z czilu pòwodów:
* pierszi mundial twòrzony przez trzë krôje;
* Meksyk òstôł pierszim krôjã, jaczi gòscy mésterstwò swiata pò rôz trzecy;
* Kanada pò rôz pierszi twòrzy chłopsczi mésterstwò swiata;
* Zjednóné Krôje twòrzą mundial pò rôz drëdżi (pò 1994 rokù).
Hewò wëniczi welowaniô:
{| class="wikitable" style="text-align: center;"
!Państwò
!Głosë
|-
| align="left" |[[Òbrôzk:Flag of Canada.svg|tło|22x22px]] [[Kanada]]
[[Òbrôzk:Flag of Mexico.svg|tło|22x22px]] [[Meksyk]]
[[Òbrôzk:Flag of the United States.svg|tło|22x22px]]
[[Zjednóné Kraje Americzi|Zjednóné Krôje Americzi]]
|'''134'''
|-
| align="left" | [[Òbrôzk:Flag of Morocco.svg|tło|22x22px]] [[Marokò]]
|65
|}
== Fòrmat rozgriwków ==
Pò rôz pierszi w historie w finałach wëstãpùje 48 reprezentacjów.
[[Òbrôzk:FIFA World Cup 2026 Draw Reception (54972845582).jpg|mały|Òficjalnô bala na mésterstwô, Trionda]]
Fòrmat òbjimùje:
* 12 grëp pò 4 karnów,
* Kòżdé karno rozgriwô 3 grëpowé mecze,
* awans zwëskiwają:
** dobiwcë grëp,
** karnë z drëdżich placów,
** òsmë nôlepszich reprezentacjów z trzecych placów,
* òd pùcharowi fazë òbrzeszëwùje pùcharowi system rozpòczinôjącë sã òd 1/16 finału (Round of 32).
== Gardë gòspòdôrzów ==
Turniér òdbëwô sã na 16 stadionach<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/fifa-unveils-stellar-line-up-of-host-cities-for-2026-world-cup FIFA unveils stellar line-up of host cities for 2026 FIFA World Cup]''. [przistãp 2026-06-29].</ref>.
=== [[Kanada]] ===
* Toronto
* Vancouver
=== [[Meksyk]] ===
* Meksyk
* Guadalajara
* Monterrey
=== [[Zjednóné Kraje Americzi|Zjednóné Krôje]] ===
* Atlanta
* Boston
* Dallas
* Houston
* Kansas City
* Los Angeles
* Miami
* [[Nowi Jork]] / East Rutherford
* Filadelfia
* San Francisco Bay Area (Santa Clara)
* Seattle
== Grëpòwa faza ==
Reprezentacje òstôłë pòdzelóné na 12 grëp òznôczonëch lëterama òd A do L. Kòżdé karno rozgriwô trzë zeńdzeniô.
=== Grëpa A ===
{{#invoke:Grëpa MS|render
|group=Grëpa A
|1=[[File:Flag of Mexico.svg|20px]] Meksyk
|1pkt=9
|1m=3
|1w=3
|1r=0
|1p=0
|1g+=6
|1g-=0
|1rg=+6
|2=[[File:Flag of South Africa.svg|20px]] Pôłniowô Afrika
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=2
|2g-=3
|2rg=-1
|3=[[File:Flag of South Korea.svg|20px]] Pôłniowô Korea
|3pkt=3
|3m=3
|3w=1
|3r=0
|3p=2
|3g+=2
|3g-=3
|3rg=-1
|4=[[File:Flag of Czech Republic.svg|20px]] Czeskô
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=2
|4g-=4
|4rg=-2
}}
=== Grëpa B ===
{{#invoke:Grëpa MS|render
|group=Grëpa B
|advance=3
|1=[[File:Flag of Switzerland.svg|20px]] Szwajcarskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=6
|1g-=3
|1rg=+3
|2=[[File:Flag of Canada.svg|20px]] Kanada
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=5
|2g-=4
|2rg=+1
|3=[[File:Flag of Bosnia and Herzegovina.svg|20px]] Bòsniô a Hercegòwina
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=4
|3g-=5
|3rg=-1
|4=[[File:Flag of Qatar.svg|20px]] Katar
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=2
|4g-=9
|4rg=-7
}}
=== Grëpa C ===
{{#invoke:Grëpa MS|render
|group=Grëpa C
|1=[[File:Flag of Brazil.svg|20px]] Brazylskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=7
|1g-=1
|1rg=+6
|2=[[File:Flag of Morocco.svg|20px]] Marokò
|2pkt=7
|2m=3
|2w=2
|2r=1
|2p=0
|2g+=6
|2g-=3
|2rg=+3
|3=[[File:Flag of Scotland.svg|20px]] Szkòckô
|3pkt=3
|3m=3
|3w=1
|3r=0
|3p=2
|3g+=2
|3g-=5
|3rg=-3
|4=[[File:Flag of Haiti.svg|20px]] Haiti
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=2
|4g-=8
|4rg=-6
}}
=== Grëpa D ===
{{#invoke:Grëpa MS|render
|group=Grëpa D
|advance=3
|1=[[File:Flag of United States.svg|20px]] USA
|1pkt=6
|1m=3
|1w=2
|1r=0
|1p=1
|1g+=8
|1g-=4
|1rg=+4
|2=[[File:Flag of Australia.svg|20px]] Aùstraliô
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=2
|2g-=2
|2rg=0
|3=[[File:Flag of Paraguay.svg|20px]] Paragwaj
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=3
|3g-=4
|3rg=-1
|4=[[File:Flag of Turkey.svg|20px]] Tëreckô
|4pkt=3
|4m=3
|4w=1
|4r=0
|4p=2
|4g+=3
|4g-=5
|4rg=-2
}}
=== Grëpa E ===
{{#invoke:Grëpa MS|render
|group=Grëpa E
|advance=3
|1=[[File:Flag of Germany.svg|20px]] Niemieckô
|1pkt=6
|1m=3
|1w=2
|1r=0
|1p=1
|1g+=10
|1g-=4
|1rg=+6
|2=[[File:Flag of Ivory Coast.svg|20px]] Ùbrzég Słoniowëch Kłów
|2pkt=6
|2m=3
|2w=2
|2r=0
|2p=1
|2g+=4
|2g-=2
|2rg=+2
|3=[[File:Flag of Ecuador.svg|20px]] Ekwador
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=2
|3g-=2
|3rg=0
|4=[[File:Flag of Curaçao.svg|20px]] Curaçao
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=1
|4g-=9
|4rg=-8
}}
=== Grëpa F ===
{{#invoke:Grëpa MS|render
|group=Grëpa F
|advance=3
|1=[[File:Flag of Netherlands.svg|20px]] Niderlandzkô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=10
|1g-=4
|1rg=+6
|2=[[File:Flag of Japan.svg|20px]] Japòńskô
|2pkt=5
|2m=3
|2w=1
|2r=2
|2p=0
|2g+=7
|2g-=3
|2rg=+4
|3=[[File:Flag of Sweden.svg|20px]] Szwedzkô
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=7
|3g-=7
|3rg=0
|4=[[File:Flag of Tunisia.svg|20px]] Tunezjô
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=2
|4g-=12
|4rg=-10
}}
=== Grëpa G ===
{{#invoke:Grëpa MS|render
|group=Grëpa G
|1=[[File:Flag of Belgium.svg|20px]] Belgiô
|1pkt=5
|1m=3
|1w=1
|1r=2
|1p=0
|1g+=6
|1g-=2
|1rg=+4
|2=[[File:Flag of Egypt.svg|20px]] Egipt
|2pkt=5
|2m=3
|2w=1
|2r=2
|2p=0
|2g+=5
|2g-=3
|2rg=+2
|3=[[File:Flag of Iran.svg|20px]] Iran
|3pkt=3
|3m=3
|3w=0
|3r=3
|3p=0
|3g+=3
|3g-=3
|3rg=0
|4=[[File:Flag of New Zealand.svg|20px]] Nowô Zelandzkô
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=4
|4g-=10
|4rg=-6
}}
=== Grëpa H ===
{{#invoke:Grëpa MS|render
|group=Grëpa H
|1=[[File:Flag of Spain.svg|20px]] Szpańskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=5
|1g-=0
|1rg=+5
|2=[[File:Flag of Cape Verde.svg|20px]] Cabo Verde
|2pkt=3
|2m=3
|2w=0
|2r=3
|2p=0
|2g+=2
|2g-=2
|2rg=0
|3=[[File:Flag of Uruguay.svg|20px]] Ùrugwaj
|3pkt=2
|3m=3
|3w=0
|3r=2
|3p=1
|3g+=3
|3g-=4
|3rg=-1
|4=[[File:Flag of Saudi Arabia.svg|20px]] Saùdijskô Arabiô
|4pkt=2
|4m=3
|4w=0
|4r=2
|4p=1
|4g+=1
|4g-=5
|4rg=-4
}}
=== Grëpa I ===
{{#invoke:Grëpa MS|render
|group=Grëpa I
|advance=3
|1=[[File:Flag of France.svg|20px]] Francëskô
|1pkt=9
|1m=3
|1w=3
|1r=0
|1p=0
|1g+=10
|1g-=2
|1rg=+8
|2=[[File:Flag of Norway.svg|20px]] Norweskô
|2pkt=6
|2m=3
|2w=2
|2r=0
|2p=1
|2g+=8
|2g-=7
|2rg=+1
|3=[[File:Flag of Senegal.svg|20px]] Senegal
|3pkt=3
|3m=3
|3w=1
|3r=0
|3p=2
|3g+=8
|3g-=6
|3rg=+2
|4=[[File:Flag of Iraq.svg|20px]] Irak
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=1
|4g-=12
|4rg=-11
}}
=== Grëpa J ===
{{#invoke:Grëpa MS|render
|group=Grëpa J
|advance=3
|1=[[File:Flag of Argentina.svg|20px]] Argentińskô
|1pkt=9
|1m=3
|1w=3
|1r=0
|1p=0
|1g+=8
|1g-=1
|1rg=+7
|2=[[File:Flag of Austria.svg|20px]] Aùstriackô
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=7
|2g-=7
|2rg=0
|3=[[File:Flag of Algeria.svg|20px]] Algierskô
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=5
|3g-=7
|3rg=-2
|4=[[File:Flag of Jordan.svg|20px]] Jordańskô
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=3
|4g-=8
|4rg=-5
}}
=== Grëpa K ===
{{#invoke:Grëpa MS|render
|group=Grëpa K
|advance=3
|1=[[File:Flag of Colombia.svg|20px]] Kòlumbiô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=4
|1g-=1
|1rg=+3
|2=[[File:Flag of Portugal.svg|20px]] Pòrtugalskô
|2pkt=5
|2m=3
|2w=1
|2r=2
|2p=0
|2g+=6
|2g-=1
|2rg=+5
|3=[[File:Flag of Democratic Republic of the Congo.svg|20px]] DRK
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=4
|3g-=3
|3rg=+1
|4=[[File:Flag of Uzbekistan.svg|20px]] Ùzbekistan
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=2
|4g-=11
|4rg=-9
}}
=== Grëpa L ===
{{#invoke:Grëpa MS|render
|group=Grëpa L
|advance=3
|1=[[File:Flag of England.svg|20px]] Anielskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=6
|1g-=2
|1rg=+4
|2=[[File:Flag of Croatia.svg|20px]] Chòrwackô
|2pkt=6
|2m=3
|2w=2
|2r=0
|2p=1
|2g+=5
|2g-=5
|2rg=0
|3=[[File:Flag of Ghana.svg|20px]] Ghana
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=2
|3g-=2
|3rg=0
|4=[[File:Flag of Panama.svg|20px]] Panama
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=0
|4g-=4
|4rg=-4
}}
=== Ranking karnów z trzecëch placów ===
{| class="wikitable" style="width:100%; table-layout:fixed; text-align:center; font-family:system-ui, Arial;"
<tr style="background:#f8f9fa;">
<th style="width:6%;">M.</th>
<th style="width:40%;">Reprezentacjô</th>
<th style="width:15%;">Grëpa</th>
<th style="width:9%;">Pkt</th>
<th style="width:6%;">M</th>
<th style="width:6%;">W</th>
<th style="width:6%;">R</th>
<th style="width:6%;">P</th>
<th style="width:6%;">+</th>
<th style="width:6%;">–</th>
<th style="width:6%;">±</th>
</tr>
|-
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Democratic Republic of the Congo.svg|20px]] DRK
| style="background:#dff6df; font-weight:bold;" | K
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | +1
|-
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Ecuador.svg|20px]] Ekwador
| style="background:#dff6df; font-weight:bold;" | E
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 0
|-
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Sweden.svg|20px]] Szwedzkô
| style="background:#dff6df; font-weight:bold;" | F
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold;" | 0
|-
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Bosnia and Herzegovina.svg|20px]] Bòsniô a Hercegòwina
| style="background:#dff6df; font-weight:bold;" | B
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 5
| style="background:#dff6df; font-weight:bold;" | −1
|-
| style="background:#dff6df; font-weight:bold;" | 5
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Paraguay.svg|20px]] Paragwaj
| style="background:#dff6df; font-weight:bold;" | D
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | −1
|-
| style="background:#dff6df; font-weight:bold;" | 6
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Ghana.svg|20px]] Ghana
| style="background:#dff6df; font-weight:bold;" | L
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 0
|-
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Algeria.svg|20px]] Algierskô
| style="background:#dff6df; font-weight:bold;" | J
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 5
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold;" | −2
|-
| style="background:#dff6df; font-weight:bold;" | 8
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Senegal.svg|20px]] Senegal
| style="background:#dff6df; font-weight:bold;" | I
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 0
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 8
| style="background:#dff6df; font-weight:bold;" | 6
| style="background:#dff6df; font-weight:bold;" | +2
|-
| 9
| style="text-align:left;" | [[File:Flag of Iran.svg|20px]] Iran
| G
| 3 || 3 || 0 || 3 || 0 || 3 || 3 || 0
|-
| 10
| style="text-align:left;" | [[File:Flag of South Korea.svg|20px]] Pôłniowô Korea
| A
| 3 || 3 || 1 || 0 || 2 || 2 || 3 || −1
|-
| 11
| style="text-align:left;" | [[File:Flag of Scotland.svg|20px]] Szkòckô
| C
| 3 || 3 || 1 || 0 || 2 || 2 || 5 || −3
|-
| 12
| style="text-align:left;" | [[File:Flag of Uruguay.svg|20px]] Ùrugwaj
| H
| 2 || 3 || 0 || 2 || 1 || 3 || 4 || −1
|}
== Pùcharowô faza ==
=== Drôbka ===
{{Turniérowô drôbka
| boldwinner=y
| RD1= 1/16 finału
| RD2= 1/8 finału
| RD1-text01=29 czerwińca – [[Gillette Stadium|Foxborough]]
| RD1-team01=[[File:Flag of Germany.svg|20px]] Niemieckô
| RD1-score01=1 (3)
| RD1-team02=[[File:Flag of Paraguay.svg|20px]] Paragwaj
| RD1-score02=1 (4)
| RD1-text03=28 czerwińca – [[SoFi Stadium|Inglewood]]
| RD1-team03=[[File:Flag of France.svg|20px]] Francëskô
| RD1-score03=3
| RD1-team04=[[File:Flag of Sweden.svg|20px]] Szwedzkô
| RD1-score04=0
| RD1-text05=3 lëpińca – [[BMO Field|Toronto]]
| RD1-team05=[[File:Flag of South Africa.svg|20px]] Pôłniowô Afrika
| RD1-team06=[[File:Flag of Canada.svg|20px]] Kanada
| RD1-score05=0
| RD1-score06=1
| RD1-text07=2 lëpińca – [[Levi’s Stadium|Santa Clara]]
| RD1-team07=[[File:Flag of Netherlands.svg|20px]] Niderlandzkô
| RD1-team08=[[File:Flag of Morocco.svg|20px]] Marokò
| RD1-score07=1 (2)
| RD1-score08=1 (3)
| RD1-text09=29 czerwińca – [[NRG Stadium|Houston]]
| RD1-team09=[[File:Flag of Portugal.svg|20px]] Pòrtugalskô
| RD1-team10=[[File:Flag of Croatia.svg|20px]] Chòrwackô
| RD1-score09=2
| RD1-score10=1
| RD1-text11=1 lëpińca – [[Estadio Azteca|Meksyk]]
| RD1-team11=[[File:Flag of Spain.svg|20px]] Szpańskô
| RD1-team12=[[File:Flag of Austria.svg|20px]] Aùstriackô
| RD1-score11=3
| RD1-score12=0
| RD1-text13=4 lëpińca – [[Hard Rock Stadium|Miami Gardens]]
| RD1-team13=[[File:Flag of United States.svg|20px]] USA
| RD1-team14=[[File:Flag of Bosnia and Herzegovina.svg|20px]] Bòsniô a Hercegòwina
| RD1-score13=2
| RD1-score14=0
| RD1-text15=3 lëpińca – [[BC Place Stadium|Vancouver]]
| RD1-team15=[[File:Flag of Belgium.svg|20px]] Belgiô
| RD1-team16=[[File:Flag of Senegal.svg|20px]] Senegal
| RD1-score15=3
| RD1-score16=2
| RD1-text17=
| RD1-team17=[[File:Flag of Brazil.svg|20px]] Brazylskô
| RD1-team18=[[File:Flag of Japan.svg|20px]] Japòńskô
| RD1-score17=2
| RD1-score18=1
| RD1-text19=
| RD1-team19=[[File:Flag of Ivory Coast.svg|20px]] Ùbrzég Słoniowëch Kłów
| RD1-team20=[[File:Flag of Norway.svg|20px]] Norweskô
| RD1-score19=1
| RD1-score20=2
| RD1-text21=
| RD1-team21=[[File:Flag of Mexico.svg|20px]] Meksyk
| RD1-team22=[[File:Flag of Ecuador.svg|20px]] Ekwador
| RD1-score21=2
| RD1-score22=0
| RD1-text23=
| RD1-team23=[[File:Flag of England.svg|20px]] Anielskô
| RD1-team24=[[File:Flag of the Democratic Republic of the Congo.svg|20px]] DRK
| RD1-score23=2
| RD1-score24=1
| RD1-text25=
| RD1-team25=[[File:Flag of Argentina.svg|20px]] Argentińskô
| RD1-team26=[[File:Flag of Cape Verde.svg|20px]] Cabo Verde
| RD1-score25=3
| RD1-score26=2
| RD1-text27=
| RD1-team27=[[File:Flag of Australia.svg|20px]] Aùstraliô
| RD1-team28=[[File:Flag of Egypt.svg|20px]] Egipt
| RD1-score27=1 (2)
| RD1-score28=1 (4)
| RD1-text29=
| RD1-team29=[[File:Flag of Switzerland.svg|20px]] Szwajcarskô
| RD1-team30=[[File:Flag of Algeria.svg|20px]] Algierskô
| RD1-score29=2
| RD1-score30=0
| RD1-text31=
| RD1-team31=[[File:Flag of Colombia.svg|20px]] Kòlumbiô
| RD1-team32=[[File:Flag of Ghana.svg|20px]] Ghana
| RD1-score31=1
| RD1-score32=0
| RD2-text01=4 lëpińca – [[Lincoln Financial Field|Filadelfia]]
| RD2-team01=[[File:Flag of Paraguay.svg|20px]] Paragwaj
| RD2-team02=[[File:Flag of France.svg|20px]] Francëskô
| RD2-score01=0
| RD2-score02=1
| RD2-text02=4 lëpińca – [[NRG Stadium|Houston]]
| RD2-team03=[[File:Flag of Canada.svg|20px]] Kanada
| RD2-team04=[[File:Flag of Morocco.svg|20px]] Marokò
| RD2-score03=0
| RD2-score04=3
| RD2-text03=6 lëpińca – [[AT&T Stadium|Arlington]]
| RD2-team05=[[File:Flag of Portugal.svg|20px]] Pòrtugalskô
| RD2-team06=[[File:Flag of Spain.svg|20px]] Szpańskô
| RD2-score05=
| RD2-score06=
| RD2-text04=7 lëpińca – [[CenturyLink Field|Seattle]]
| RD2-team07=[[File:Flag of USA.svg|20px]] USA
| RD2-team08=[[File:Flag of Belgium.svg|20px]] Belgiô
| RD2-score07=
| RD2-score08=
| RD2-text05=5 lëpińca – [[MetLife Stadium|East Rutherford]]
| RD2-team09=[[File:Flag of Brazil.svg|20px]] Brazylskô
| RD2-team10=[[File:Flag of Norway.svg|20px]] Norweskô
| RD2-score09=
| RD2-score10=
| RD2-text06=6 lëpińca – [[Estadio Azteca|Meksyk]]
| RD2-team11=[[File:Flag of Mexico.svg|20px]] Meksyk
| RD2-team12=[[File:Flag of England.svg|20px]] Anielskô
| RD2-score11=
| RD2-score12=
| RD2-text07=7 lëpińca – [[Mercedes-Benz Stadium|Atlanta]]
| RD2-team13=[[File:Flag of Argentina.svg|20px]] Argentińskô
| RD2-team14=[[File:Flag of Egypt.svg|20px]] Egipt
| RD2-score13=
| RD2-score14=
| RD2-text08=7 lëpińca – [[BC Place Stadium|Vancouver]]
| RD2-team15=[[File:Flag of Switzerland.svg|20px]] Szwajcarskô
| RD2-team16=[[File:Flag of Colombia.svg|20px]] Kòlumbiô
| RD2-score15=
| RD2-score16=
| RD3-text01=9 lëpińca – [[Gillette Stadium|Foxborough]]
| RD3-team01=[[File:Flag of Morocco.svg|20px]] Marokò
| RD3-team02=[[File:Flag of France.svg|20px]] Francëskô
| RD3-score01=
| RD3-score02=
| RD3-text02=10 lëpińca – [[SoFi Stadium|Inglewood]]
| RD3-team03=Dobiwca meczu 93
| RD3-team04=Dobiwca meczu 94
| RD3-score03=
| RD3-score04=
| RD3-text03=11 lëpińca – [[Hard Rock Stadium|Miami Gardens]]
| RD3-team05=Dobiwca meczu 91
| RD3-team06=Dobiwca meczu 92
| RD3-score05=
| RD3-score06=
| RD3-text04=12 lëpińca – [[Arrowhead Stadium|Kansas City]]
| RD3-team07=Dobiwca meczu 95
| RD3-team08=Dobiwca meczu 96
| RD3-score07=
| RD3-score08=
| RD4-text01=14 lëpińca – [[AT&T Stadium|Arlington]]
| RD4-team01=Dobiwca meczu 97
| RD4-team02=Dobiwca meczu 98
| RD4-score01=
| RD4-score02=
| RD4-text02=15 lëpińca – [[Mercedes-Benz Stadium|Atlanta]]
| RD4-team03=Dobiwca meczu 99
| RD4-team04=Dobiwca meczu 100
| RD4-score03=
| RD4-score04=
| RD5-text01=19 lëpińca – [[MetLife Stadium|East Rutherford]]
| RD5-team01=Dobiwca meczu 101
| RD5-team02=Dobiwca meczu 102
| RD5-score01=
| RD5-score02=
| RD5b-text01=18 lëpińca – [[Hard Rock Stadium|Miami Gardens]]
| RD5b-team01=Przerżnóny meczu 101
| RD5b-team02=Przerżnóny meczu 102
| RD5b-score01=
| RD5b-score02=
}}
=== 1/16 finału ===
{{fùsbalowi mecz
| etap = Mecz 73
| data = 28 czerwińca 2026, 21:00
| stadión = SoFi Stadium, Los Angeles
| druzyna1 = Pôłniowô Afrika
| fana1 = RSA
| wënik1 = 0
| druzyna2 = Kanada
| fana2 = Canada
| wënik2 = 1
| gòle2 = Eustáquio 90+2
| òbzérôczë = 69 237
| sãdza = João Pinheiro
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 74
| data = 29 czerwińca 2026, 19:00
| stadión = NRG Stadium, Houston
| druzyna1 = Brazylskô
| fana1 = Brazil
| wënik1 = 2
| druzyna2 = Japòńskô
| fana2 = Japan
| wënik2 = 1
| gòle1 = Casemiro 56’, Martinelli 90+5
| gòle2 = Sano 29’
| òbzérôczë = 68 777
| sãdza = Maurizio Mariani
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 75
| data = 29 czerwińca 2026, 22:30
| stadión = Gillette Stadium, Boston
| druzyna1 = Niemieckô
| fana1 = Germany
| wënik1 = 1
| druzyna2 = Paragwaj
| fana2 = Paraguay
| wënik2 = 1
| karné = 3-4
| gòle1 = Havertz 54’
| gòle2 = Enciso 42’
| òbzérôczë = 63 945
| sãdza = Jalal Jayed
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 76
| data = 30 czerwińca 2026, 03:00
| stadión = Estadio BBVA, Monterrey
| druzyna1 = Niderlandzkô
| fana1 = Netherlands
| wënik1 = 1
| gòle1 = Gakpo 72’
| druzyna2 = Marokò
| fana2 = Morocco
| wënik2 = 1
| karné = 2-3
| gòle2 = Diop 90+1
| òbzérôczë = 68 777
| sãdza = Wilton Sampaio
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 77
| data = 30 czerwińca 2026, 19:00
| stadión = AT&T Stadium, Arlington
| druzyna1 = Ùbrzég Słoniowëch Kłów
| fana1 = Ivory Coast
| wënik1 = 1
| gòle1 = Diallo 74’
| druzyna2 = Norweskô
| fana2 = Norway
| wënik2 = 2
| gòle2 = Nusa 39’, Haaland 86’
| òbzérôczë = 69 665
| sãdza = Jesús Valenzuela
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 78
| data = 30 czerwińca 2026, 23:00
| stadión = MetLife Stadium, East Rutherford
| druzyna1 = Francëskô
| fana1 = France
| wënik1 = 3
| druzyna2 = Szwedzkô
| fana2 = Sweden
| wënik2 = 0
| gòle1 = Mbappé 45’, 74’; Barcola 53’
| òbzérôczë = 80 663
| sãdza = Danny Makkelie
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 79
| data = 1 lëpińca 2026, 03:00
| stadión = Estadio Azteca, Meksyk
| druzyna1 = Meksyk
| fana1 = Mexico
| wënik1 = 2
| druzyna2 = Ekwador
| fana2 = Ecuador
| wënik2 = 0
| gòle1 = Quiñones 22’, Jiménez 31’
| òbzérôczë = 80 824
| sãdza = Slavko Vinčić
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 80
| data = 1 lëpińca 2026, 18:00
| stadión = Mercedes-Benz Stadium, Atlanta
| druzyna1 = Anielskô
| fana1 = England
| wënik1 = 2
| druzyna2 = Demòkraticznô Repùblika Kònga
| fana2 = Democratic Republic of the Congo
| wënik2 = 1
| gòle1 = Kane 75’, 86’
| gòle2 = Cipenga 7’
| òbzérôczë = 68 239
| sãdza = Adham Makhadmeh
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 81
| data = 1 lëpińca 2026, 22:00
| stadión = Lumen Field, Seattle
| druzyna1 = Belgiô
| fana1 = Belgium
| wënik1 = 3
| gòle1 = Lukaku 86’; Tielemans 89’, 120+5'
| druzyna2 = Senegal
| fana2 = Senegal
| wënik2 = 2
| gòle2 = Diarra 24’, Sarr 51’
| òbzérôczë = 66 925
| sãdza = Said Martínez
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 82
| data = 2 lëpińca 2026, 02:00
| stadión = Levi’s Stadium, Santa Clara
| druzyna1 = USA
| fana1 = USA
| wënik1 = 2
| gòle1 = Balogun 78’, Tillman 82'
| druzyna2 = Bòsniô a Hercegòwina
| fana2 = Bosnia and Herzegovina
| wënik2 = 0
| òbzérôczë = 68 827
| sãdza = Raphael Claus
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 83
| data = 2 lëpińca 2026, 22:00
| stadión = SoFi Stadium, Los Angeles
| druzyna1 = Szpańskô
| fana1 = Spain
| wënik1 = 3
| gòle1 = Oyarzabal 36', 89'; Porro 66'
| druzyna2 = Aùstriackô
| fana2 = Austria
| wënik2 = 0
| òbzérôczë = 70 492
| sãdza = Glenn Nyberg
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 84
| data = 2 lëpińca 2026, 18:00
| stadión = GEHA Field at Arrowhead Stadium, Kansas City
| druzyna1 = Pòrtugalskô
| fana1 = Portugal
| wënik1 = 2
| gòle1 = Cristiano Ronaldo 68' (k.), Gonçalo Ramos 90+4'
| druzyna2 = Chòrwackô
| fana2 = Croatia
| wënik2 = 1
| gòle2 = Perišić 41'
| òbzérôczë = 43 036
| sãdza = Espen Eskas
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 85
| data = 3 lëpińca 2026, 02:00
| stadión = BC Place, Vancouver
| druzyna1 = Szwajcarskô
| fana1 = Switzerland
| wënik1 = 2
| gòle1 = Embolo 10'; Ndoye 46'
| druzyna2 = Algerskô
| fana2 = Algeria
| wënik2 = 0
| òbzérôczë = 52 497
| sãdza = Yael Falcon Perez
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 86
| data = 3 lëpińca 2026, 20:00
| stadión = AT&T Stadium, Arlington
| druzyna1 = Aùstraliô
| fana1 = Australia
| wënik1 = 1
| druzyna2 = Egipt
| fana2 = Egypt
| wënik2 = 1
| karné = 2-4
| gòle1 = Hany 55'
| gòle2 = Ashour 13'
| òbzérôczë = 70 244
| sãdza = Gustavo Tejera
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 87
| data = 3 lëpińca 2026, 22:00
| stadión = Hard Rock Stadium, Miami Gardens
| druzyna1 = Argentińskô
| fana1 = Argentina
| wënik1 = 3
| gòle1 = Messi 29'; Lautaro Martínez 92'; Diney 111'
| druzyna2 = Cabo Verde
| fana2 = Cape Verde
| wënik2 = 2
| gòle2 = D. Duarte 59'; Lopes Cabral 103'
| òbzérôczë = 64 478
| sãdza = Drew Fischer
}}
<br>
-------
{{fùsbalowi mecz
| etap = Mecz 88
| data = 4 lëpińca 2026, 02:30
| stadión = GEHA Field at Arrowhead Stadium, Kansas City
| druzyna1 = Kòlumbiô
| fana1 = Colombia
| wënik1 = 1
| gòle1 = J. Arias 14'
| druzyna2 = Ghana
| fana2 = Ghana
| wënik2 = 0
| òbzérôczë = 69 045
| sãdza = Clément Turpin
}}
=== 1/8 finału ===
{{fùsbalowi mecz
| etap = Mecz 89
| data = 4 lëpińca 2026, 13:00
| stadión = NRG Stadium, Houston
| druzyna1 = Kanada
| fana1 = Canada
| wënik1 = 0
| druzyna2 = Marokò
| fana2 = Morocco
| wënik2 = 3
| gòle2 = Ounahi 50’, 82’; Rahimi 90+8’
| òbzérôczë = 68 777
| sãdza = Michael Oliver
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 90
| data = 4 lëpińca 2026, 21:00
| stadión = MetLife Stadium, East Rutherford
| druzyna1 = Paragwaj
| fana1 = Paraguay
| wënik1 = 0
| druzyna2 = Francëskô
| fana2 = France
| wënik2 = 1
| gòle2 = Mbappé 70' (k.)
| òbzérôczë = 68 324
| sãdza = Ilgiz Tantashev
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 91
| data = 5 lëpińca 2026, 22:00
| stadión = NRG Stadium, Houston
| druzyna1 = Brazylskô
| fana1 = Brazil
| wënik1 = 1
| gòle1 = Neymar 90+10'
| druzyna2 = Norweskô
| fana2 = Norway
| wënik2 = 2
| gòle2 = Haaland 79', 90'
| òbzérôczë = 80 663
| sãdza = Ismail Elfath
}}
<br>
'''Bôczënk:''' jinfòrmacje zrzeszoné z pòstãpnyma zeńdzeniama mdą aktualizowóné przez całi czas.
== Finał ==
Finał òdbëwa sã [[19 lëpińca]] 2026 rokù na stadionie ''MetLife Stadium'' w East Rutherford (aglomeracjô [[Nowi Jork|Nowégò Jorkù]]).
== Òbaczë téż ==
{{#categorytree:Nożnô bala|hideroot=on|mode=pages}}
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
{{Kòntrola}}
[[Kategòrëjô:Nożnô bala]]
h9qaao8ounfc9caxa5nq92t0il5qkn8
204491
204490
2026-07-05T22:26:26Z
HaxelKaszëba
17492
ùprawkô
204491
wikitext
text/x-wiki
{{Spòrtowi Turniér infobox|gòspòdôrzë=[[Òbrôzk:Flag of Canada.svg|tło|22x22px]] [[Kanada]]
[[Òbrôzk:Flag of Mexico.svg|tło|22x22px]] [[Meksyk]]
[[Òbrôzk:Flag of the United States.svg|tło|22x22px]] [[Zjednóné Kraje Americzi|Zjednóné Krôje Americzi]]|data=11 czerwińca 2026|pòzwa=Mésterstwò Swiata w fùsbalë 2026<br>FIFA World Cup 2026<br>Coupe du monde de football de 2026<br>Copa Mundial de Fútbol 2026|òbrôzk=2026 FIFA World Cup emblem (with wordmark).svg|reprezentacje=48|stadiónë=16|mecze=91|gòle= 264|ùszłô=[[Mésterstwò Swiata w fùsbalë 2022|2022]]|aktualné=2026]}}
'''XXIII Mésterstwò Swiata w fùsbalë 2026''' – 23. edicjô mésterstwô swiata twòrzoné przez [[FIFA]]. Turniér òdbëwô sã òd [[11 czerwińca]] do [[19 lëpińca]] 2026 rokù na stadionach w [[Kanada|Kanadze]], [[Meksyk|Meksykù]] a [[Zjednóné Kraje Americzi|Zjednónëch Krôjach]]<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026 FIFA World Cup 2026 Official Website]''. [przistãp 2026-06-29].</ref>. Je to pierszi mùndial twòrzony wespólnie przez trzë krôje òrôz pierszi w historie z dzélã 48 nôrodnëch reprezentacjów.
Nowi fòrmat turniéru òbjimùje 12 grëp pò szterëch karnów, z jaczich do pùcharowi fazë awansëje dwie nôlepszé reprezentacje z kòżdi grëpë òrôz òsmë nôlepszich karnów z trzecëch placów. Òznôczô to rozegranié rekordowi lëczbë 104 zeńdzeniów<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/fifa-council-approves-international-match-calendar-new-format FIFA Council approves international match calendar and 104-match World Cup format]''. [przistãp 2026-06-29].</ref>.
== Welënk gòspòdôrzów ==
[[Òbrôzk:2026 world cup.svg|mały|Wëniczi w òsoblëwëch krôjach]]
13 czerwińca 2018 rokù òb czas Kòngresu FIFA w Mòskwie dobéła wespólna kandidatura Kanadë, Meksykù a Zjednónëch Krôjów, przedobiwôjącë bédënk [[Marokò|Maroka]]<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/united-2026-bid-wins-hosts-fifa-world-cup United 2026 bid voted hosts of the 2026 FIFA World Cup]''. [przistãp 2026-06-29].</ref>.
Turniér je historiczny z czilu pòwodów:
* pierszi mundial twòrzony przez trzë krôje;
* Meksyk òstôł pierszim krôjã, jaczi gòscy mésterstwò swiata pò rôz trzecy;
* Kanada pò rôz pierszi twòrzy chłopsczi mésterstwò swiata;
* Zjednóné Krôje twòrzą mundial pò rôz drëdżi (pò 1994 rokù).
Hewò wëniczi welowaniô:
{| class="wikitable" style="text-align: center;"
!Państwò
!Głosë
|-
| align="left" |[[Òbrôzk:Flag of Canada.svg|tło|22x22px]] [[Kanada]]
[[Òbrôzk:Flag of Mexico.svg|tło|22x22px]] [[Meksyk]]
[[Òbrôzk:Flag of the United States.svg|tło|22x22px]]
[[Zjednóné Kraje Americzi|Zjednóné Krôje Americzi]]
|'''134'''
|-
| align="left" | [[Òbrôzk:Flag of Morocco.svg|tło|22x22px]] [[Marokò]]
|65
|}
== Fòrmat rozgriwków ==
Pò rôz pierszi w historie w finałach wëstãpùje 48 reprezentacjów.
[[Òbrôzk:FIFA World Cup 2026 Draw Reception (54972845582).jpg|mały|Òficjalnô bala na mésterstwô, Trionda]]
Fòrmat òbjimùje:
* 12 grëp pò 4 karnów,
* Kòżdé karno rozgriwô 3 grëpowé mecze,
* awans zwëskiwają:
** dobiwcë grëp,
** karnë z drëdżich placów,
** òsmë nôlepszich reprezentacjów z trzecych placów,
* òd pùcharowi fazë òbrzeszëwùje pùcharowi system rozpòczinôjącë sã òd 1/16 finału (Round of 32).
== Gardë gòspòdôrzów ==
Turniér òdbëwô sã na 16 stadionach<ref>FIFA: ''[https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/fifa-unveils-stellar-line-up-of-host-cities-for-2026-world-cup FIFA unveils stellar line-up of host cities for 2026 FIFA World Cup]''. [przistãp 2026-06-29].</ref>.
=== [[Kanada]] ===
* Toronto
* Vancouver
=== [[Meksyk]] ===
* Meksyk
* Guadalajara
* Monterrey
=== [[Zjednóné Kraje Americzi|Zjednóné Krôje]] ===
* Atlanta
* Boston
* Dallas
* Houston
* Kansas City
* Los Angeles
* Miami
* [[Nowi Jork]] / East Rutherford
* Filadelfia
* San Francisco Bay Area (Santa Clara)
* Seattle
== Grëpòwa faza ==
Reprezentacje òstôłë pòdzelóné na 12 grëp òznôczonëch lëterama òd A do L. Kòżdé karno rozgriwô trzë zeńdzeniô.
=== Grëpa A ===
{{#invoke:Grëpa MS|render
|group=Grëpa A
|1=[[File:Flag of Mexico.svg|20px]] Meksyk
|1pkt=9
|1m=3
|1w=3
|1r=0
|1p=0
|1g+=6
|1g-=0
|1rg=+6
|2=[[File:Flag of South Africa.svg|20px]] Pôłniowô Afrika
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=2
|2g-=3
|2rg=-1
|3=[[File:Flag of South Korea.svg|20px]] Pôłniowô Korea
|3pkt=3
|3m=3
|3w=1
|3r=0
|3p=2
|3g+=2
|3g-=3
|3rg=-1
|4=[[File:Flag of Czech Republic.svg|20px]] Czeskô
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=2
|4g-=4
|4rg=-2
}}
=== Grëpa B ===
{{#invoke:Grëpa MS|render
|group=Grëpa B
|advance=3
|1=[[File:Flag of Switzerland.svg|20px]] Szwajcarskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=6
|1g-=3
|1rg=+3
|2=[[File:Flag of Canada.svg|20px]] Kanada
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=5
|2g-=4
|2rg=+1
|3=[[File:Flag of Bosnia and Herzegovina.svg|20px]] Bòsniô a Hercegòwina
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=4
|3g-=5
|3rg=-1
|4=[[File:Flag of Qatar.svg|20px]] Katar
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=2
|4g-=9
|4rg=-7
}}
=== Grëpa C ===
{{#invoke:Grëpa MS|render
|group=Grëpa C
|1=[[File:Flag of Brazil.svg|20px]] Brazylskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=7
|1g-=1
|1rg=+6
|2=[[File:Flag of Morocco.svg|20px]] Marokò
|2pkt=7
|2m=3
|2w=2
|2r=1
|2p=0
|2g+=6
|2g-=3
|2rg=+3
|3=[[File:Flag of Scotland.svg|20px]] Szkòckô
|3pkt=3
|3m=3
|3w=1
|3r=0
|3p=2
|3g+=2
|3g-=5
|3rg=-3
|4=[[File:Flag of Haiti.svg|20px]] Haiti
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=2
|4g-=8
|4rg=-6
}}
=== Grëpa D ===
{{#invoke:Grëpa MS|render
|group=Grëpa D
|advance=3
|1=[[File:Flag of United States.svg|20px]] USA
|1pkt=6
|1m=3
|1w=2
|1r=0
|1p=1
|1g+=8
|1g-=4
|1rg=+4
|2=[[File:Flag of Australia.svg|20px]] Aùstraliô
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=2
|2g-=2
|2rg=0
|3=[[File:Flag of Paraguay.svg|20px]] Paragwaj
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=3
|3g-=4
|3rg=-1
|4=[[File:Flag of Turkey.svg|20px]] Tëreckô
|4pkt=3
|4m=3
|4w=1
|4r=0
|4p=2
|4g+=3
|4g-=5
|4rg=-2
}}
=== Grëpa E ===
{{#invoke:Grëpa MS|render
|group=Grëpa E
|advance=3
|1=[[File:Flag of Germany.svg|20px]] Niemieckô
|1pkt=6
|1m=3
|1w=2
|1r=0
|1p=1
|1g+=10
|1g-=4
|1rg=+6
|2=[[File:Flag of Ivory Coast.svg|20px]] Ùbrzég Słoniowëch Kłów
|2pkt=6
|2m=3
|2w=2
|2r=0
|2p=1
|2g+=4
|2g-=2
|2rg=+2
|3=[[File:Flag of Ecuador.svg|20px]] Ekwador
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=2
|3g-=2
|3rg=0
|4=[[File:Flag of Curaçao.svg|20px]] Curaçao
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=1
|4g-=9
|4rg=-8
}}
=== Grëpa F ===
{{#invoke:Grëpa MS|render
|group=Grëpa F
|advance=3
|1=[[File:Flag of Netherlands.svg|20px]] Niderlandzkô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=10
|1g-=4
|1rg=+6
|2=[[File:Flag of Japan.svg|20px]] Japòńskô
|2pkt=5
|2m=3
|2w=1
|2r=2
|2p=0
|2g+=7
|2g-=3
|2rg=+4
|3=[[File:Flag of Sweden.svg|20px]] Szwedzkô
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=7
|3g-=7
|3rg=0
|4=[[File:Flag of Tunisia.svg|20px]] Tunezjô
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=2
|4g-=12
|4rg=-10
}}
=== Grëpa G ===
{{#invoke:Grëpa MS|render
|group=Grëpa G
|1=[[File:Flag of Belgium.svg|20px]] Belgiô
|1pkt=5
|1m=3
|1w=1
|1r=2
|1p=0
|1g+=6
|1g-=2
|1rg=+4
|2=[[File:Flag of Egypt.svg|20px]] Egipt
|2pkt=5
|2m=3
|2w=1
|2r=2
|2p=0
|2g+=5
|2g-=3
|2rg=+2
|3=[[File:Flag of Iran.svg|20px]] Iran
|3pkt=3
|3m=3
|3w=0
|3r=3
|3p=0
|3g+=3
|3g-=3
|3rg=0
|4=[[File:Flag of New Zealand.svg|20px]] Nowô Zelandzkô
|4pkt=1
|4m=3
|4w=0
|4r=1
|4p=2
|4g+=4
|4g-=10
|4rg=-6
}}
=== Grëpa H ===
{{#invoke:Grëpa MS|render
|group=Grëpa H
|1=[[File:Flag of Spain.svg|20px]] Szpańskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=5
|1g-=0
|1rg=+5
|2=[[File:Flag of Cape Verde.svg|20px]] Cabo Verde
|2pkt=3
|2m=3
|2w=0
|2r=3
|2p=0
|2g+=2
|2g-=2
|2rg=0
|3=[[File:Flag of Uruguay.svg|20px]] Ùrugwaj
|3pkt=2
|3m=3
|3w=0
|3r=2
|3p=1
|3g+=3
|3g-=4
|3rg=-1
|4=[[File:Flag of Saudi Arabia.svg|20px]] Saùdijskô Arabiô
|4pkt=2
|4m=3
|4w=0
|4r=2
|4p=1
|4g+=1
|4g-=5
|4rg=-4
}}
=== Grëpa I ===
{{#invoke:Grëpa MS|render
|group=Grëpa I
|advance=3
|1=[[File:Flag of France.svg|20px]] Francëskô
|1pkt=9
|1m=3
|1w=3
|1r=0
|1p=0
|1g+=10
|1g-=2
|1rg=+8
|2=[[File:Flag of Norway.svg|20px]] Norweskô
|2pkt=6
|2m=3
|2w=2
|2r=0
|2p=1
|2g+=8
|2g-=7
|2rg=+1
|3=[[File:Flag of Senegal.svg|20px]] Senegal
|3pkt=3
|3m=3
|3w=1
|3r=0
|3p=2
|3g+=8
|3g-=6
|3rg=+2
|4=[[File:Flag of Iraq.svg|20px]] Irak
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=1
|4g-=12
|4rg=-11
}}
=== Grëpa J ===
{{#invoke:Grëpa MS|render
|group=Grëpa J
|advance=3
|1=[[File:Flag of Argentina.svg|20px]] Argentińskô
|1pkt=9
|1m=3
|1w=3
|1r=0
|1p=0
|1g+=8
|1g-=1
|1rg=+7
|2=[[File:Flag of Austria.svg|20px]] Aùstriackô
|2pkt=4
|2m=3
|2w=1
|2r=1
|2p=1
|2g+=7
|2g-=7
|2rg=0
|3=[[File:Flag of Algeria.svg|20px]] Algierskô
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=5
|3g-=7
|3rg=-2
|4=[[File:Flag of Jordan.svg|20px]] Jordańskô
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=3
|4g-=8
|4rg=-5
}}
=== Grëpa K ===
{{#invoke:Grëpa MS|render
|group=Grëpa K
|advance=3
|1=[[File:Flag of Colombia.svg|20px]] Kòlumbiô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=4
|1g-=1
|1rg=+3
|2=[[File:Flag of Portugal.svg|20px]] Pòrtugalskô
|2pkt=5
|2m=3
|2w=1
|2r=2
|2p=0
|2g+=6
|2g-=1
|2rg=+5
|3=[[File:Flag of Democratic Republic of the Congo.svg|20px]] DRK
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=4
|3g-=3
|3rg=+1
|4=[[File:Flag of Uzbekistan.svg|20px]] Ùzbekistan
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=2
|4g-=11
|4rg=-9
}}
=== Grëpa L ===
{{#invoke:Grëpa MS|render
|group=Grëpa L
|advance=3
|1=[[File:Flag of England.svg|20px]] Anielskô
|1pkt=7
|1m=3
|1w=2
|1r=1
|1p=0
|1g+=6
|1g-=2
|1rg=+4
|2=[[File:Flag of Croatia.svg|20px]] Chòrwackô
|2pkt=6
|2m=3
|2w=2
|2r=0
|2p=1
|2g+=5
|2g-=5
|2rg=0
|3=[[File:Flag of Ghana.svg|20px]] Ghana
|3pkt=4
|3m=3
|3w=1
|3r=1
|3p=1
|3g+=2
|3g-=2
|3rg=0
|4=[[File:Flag of Panama.svg|20px]] Panama
|4pkt=0
|4m=3
|4w=0
|4r=0
|4p=3
|4g+=0
|4g-=4
|4rg=-4
}}
=== Ranking karnów z trzecëch placów ===
{| class="wikitable" style="width:100%; table-layout:fixed; text-align:center; font-family:system-ui, Arial;"
<tr style="background:#f8f9fa;">
<th style="width:6%;">M.</th>
<th style="width:40%;">Reprezentacjô</th>
<th style="width:15%;">Grëpa</th>
<th style="width:9%;">Pkt</th>
<th style="width:6%;">M</th>
<th style="width:6%;">W</th>
<th style="width:6%;">R</th>
<th style="width:6%;">P</th>
<th style="width:6%;">+</th>
<th style="width:6%;">–</th>
<th style="width:6%;">±</th>
</tr>
|-
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Democratic Republic of the Congo.svg|20px]] DRK
| style="background:#dff6df; font-weight:bold;" | K
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | +1
|-
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Ecuador.svg|20px]] Ekwador
| style="background:#dff6df; font-weight:bold;" | E
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 0
|-
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Sweden.svg|20px]] Szwedzkô
| style="background:#dff6df; font-weight:bold;" | F
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold;" | 0
|-
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Bosnia and Herzegovina.svg|20px]] Bòsniô a Hercegòwina
| style="background:#dff6df; font-weight:bold;" | B
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 5
| style="background:#dff6df; font-weight:bold;" | −1
|-
| style="background:#dff6df; font-weight:bold;" | 5
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Paraguay.svg|20px]] Paragwaj
| style="background:#dff6df; font-weight:bold;" | D
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | −1
|-
| style="background:#dff6df; font-weight:bold;" | 6
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Ghana.svg|20px]] Ghana
| style="background:#dff6df; font-weight:bold;" | L
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 0
|-
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Algeria.svg|20px]] Algierskô
| style="background:#dff6df; font-weight:bold;" | J
| style="background:#dff6df; font-weight:bold;" | 4
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 5
| style="background:#dff6df; font-weight:bold;" | 7
| style="background:#dff6df; font-weight:bold;" | −2
|-
| style="background:#dff6df; font-weight:bold;" | 8
| style="background:#dff6df; font-weight:bold; text-align:left;" | [[File:Flag of Senegal.svg|20px]] Senegal
| style="background:#dff6df; font-weight:bold;" | I
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 3
| style="background:#dff6df; font-weight:bold;" | 1
| style="background:#dff6df; font-weight:bold;" | 0
| style="background:#dff6df; font-weight:bold;" | 2
| style="background:#dff6df; font-weight:bold;" | 8
| style="background:#dff6df; font-weight:bold;" | 6
| style="background:#dff6df; font-weight:bold;" | +2
|-
| 9
| style="text-align:left;" | [[File:Flag of Iran.svg|20px]] Iran
| G
| 3 || 3 || 0 || 3 || 0 || 3 || 3 || 0
|-
| 10
| style="text-align:left;" | [[File:Flag of South Korea.svg|20px]] Pôłniowô Korea
| A
| 3 || 3 || 1 || 0 || 2 || 2 || 3 || −1
|-
| 11
| style="text-align:left;" | [[File:Flag of Scotland.svg|20px]] Szkòckô
| C
| 3 || 3 || 1 || 0 || 2 || 2 || 5 || −3
|-
| 12
| style="text-align:left;" | [[File:Flag of Uruguay.svg|20px]] Ùrugwaj
| H
| 2 || 3 || 0 || 2 || 1 || 3 || 4 || −1
|}
== Pùcharowô faza ==
=== Drôbka ===
{{Turniérowô drôbka
| boldwinner=y
| RD1= 1/16 finału
| RD2= 1/8 finału
| RD1-text01=29 czerwińca – [[Gillette Stadium|Foxborough]]
| RD1-team01=[[File:Flag of Germany.svg|20px]] Niemieckô
| RD1-score01=1 (3)
| RD1-team02=[[File:Flag of Paraguay.svg|20px]] Paragwaj
| RD1-score02=1 (4)
| RD1-text03=28 czerwińca – [[SoFi Stadium|Inglewood]]
| RD1-team03=[[File:Flag of France.svg|20px]] Francëskô
| RD1-score03=3
| RD1-team04=[[File:Flag of Sweden.svg|20px]] Szwedzkô
| RD1-score04=0
| RD1-text05=3 lëpińca – [[BMO Field|Toronto]]
| RD1-team05=[[File:Flag of South Africa.svg|20px]] Pôłniowô Afrika
| RD1-team06=[[File:Flag of Canada.svg|20px]] Kanada
| RD1-score05=0
| RD1-score06=1
| RD1-text07=2 lëpińca – [[Levi’s Stadium|Santa Clara]]
| RD1-team07=[[File:Flag of Netherlands.svg|20px]] Niderlandzkô
| RD1-team08=[[File:Flag of Morocco.svg|20px]] Marokò
| RD1-score07=1 (2)
| RD1-score08=1 (3)
| RD1-text09=29 czerwińca – [[NRG Stadium|Houston]]
| RD1-team09=[[File:Flag of Portugal.svg|20px]] Pòrtugalskô
| RD1-team10=[[File:Flag of Croatia.svg|20px]] Chòrwackô
| RD1-score09=2
| RD1-score10=1
| RD1-text11=1 lëpińca – [[Estadio Azteca|Meksyk]]
| RD1-team11=[[File:Flag of Spain.svg|20px]] Szpańskô
| RD1-team12=[[File:Flag of Austria.svg|20px]] Aùstriackô
| RD1-score11=3
| RD1-score12=0
| RD1-text13=4 lëpińca – [[Hard Rock Stadium|Miami Gardens]]
| RD1-team13=[[File:Flag of United States.svg|20px]] USA
| RD1-team14=[[File:Flag of Bosnia and Herzegovina.svg|20px]] Bòsniô a Hercegòwina
| RD1-score13=2
| RD1-score14=0
| RD1-text15=3 lëpińca – [[BC Place Stadium|Vancouver]]
| RD1-team15=[[File:Flag of Belgium.svg|20px]] Belgiô
| RD1-team16=[[File:Flag of Senegal.svg|20px]] Senegal
| RD1-score15=3
| RD1-score16=2
| RD1-text17=
| RD1-team17=[[File:Flag of Brazil.svg|20px]] Brazylskô
| RD1-team18=[[File:Flag of Japan.svg|20px]] Japòńskô
| RD1-score17=2
| RD1-score18=1
| RD1-text19=
| RD1-team19=[[File:Flag of Ivory Coast.svg|20px]] Ùbrzég Słoniowëch Kłów
| RD1-team20=[[File:Flag of Norway.svg|20px]] Norweskô
| RD1-score19=1
| RD1-score20=2
| RD1-text21=
| RD1-team21=[[File:Flag of Mexico.svg|20px]] Meksyk
| RD1-team22=[[File:Flag of Ecuador.svg|20px]] Ekwador
| RD1-score21=2
| RD1-score22=0
| RD1-text23=
| RD1-team23=[[File:Flag of England.svg|20px]] Anielskô
| RD1-team24=[[File:Flag of the Democratic Republic of the Congo.svg|20px]] DRK
| RD1-score23=2
| RD1-score24=1
| RD1-text25=
| RD1-team25=[[File:Flag of Argentina.svg|20px]] Argentińskô
| RD1-team26=[[File:Flag of Cape Verde.svg|20px]] Cabo Verde
| RD1-score25=3
| RD1-score26=2
| RD1-text27=
| RD1-team27=[[File:Flag of Australia.svg|20px]] Aùstraliô
| RD1-team28=[[File:Flag of Egypt.svg|20px]] Egipt
| RD1-score27=1 (2)
| RD1-score28=1 (4)
| RD1-text29=
| RD1-team29=[[File:Flag of Switzerland.svg|20px]] Szwajcarskô
| RD1-team30=[[File:Flag of Algeria.svg|20px]] Algierskô
| RD1-score29=2
| RD1-score30=0
| RD1-text31=
| RD1-team31=[[File:Flag of Colombia.svg|20px]] Kòlumbiô
| RD1-team32=[[File:Flag of Ghana.svg|20px]] Ghana
| RD1-score31=1
| RD1-score32=0
| RD2-text01=4 lëpińca – [[Lincoln Financial Field|Filadelfia]]
| RD2-team01=[[File:Flag of Paraguay.svg|20px]] Paragwaj
| RD2-team02=[[File:Flag of France.svg|20px]] Francëskô
| RD2-score01=0
| RD2-score02=1
| RD2-text02=4 lëpińca – [[NRG Stadium|Houston]]
| RD2-team03=[[File:Flag of Canada.svg|20px]] Kanada
| RD2-team04=[[File:Flag of Morocco.svg|20px]] Marokò
| RD2-score03=0
| RD2-score04=3
| RD2-text03=6 lëpińca – [[AT&T Stadium|Arlington]]
| RD2-team05=[[File:Flag of Portugal.svg|20px]] Pòrtugalskô
| RD2-team06=[[File:Flag of Spain.svg|20px]] Szpańskô
| RD2-score05=
| RD2-score06=
| RD2-text04=7 lëpińca – [[CenturyLink Field|Seattle]]
| RD2-team07=[[File:Flag of USA.svg|20px]] USA
| RD2-team08=[[File:Flag of Belgium.svg|20px]] Belgiô
| RD2-score07=
| RD2-score08=
| RD2-text05=5 lëpińca – [[MetLife Stadium|East Rutherford]]
| RD2-team09=[[File:Flag of Brazil.svg|20px]] Brazylskô
| RD2-team10=[[File:Flag of Norway.svg|20px]] Norweskô
| RD2-score09=1
| RD2-score10=2
| RD2-text06=6 lëpińca – [[Estadio Azteca|Meksyk]]
| RD2-team11=[[File:Flag of Mexico.svg|20px]] Meksyk
| RD2-team12=[[File:Flag of England.svg|20px]] Anielskô
| RD2-score11=
| RD2-score12=
| RD2-text07=7 lëpińca – [[Mercedes-Benz Stadium|Atlanta]]
| RD2-team13=[[File:Flag of Argentina.svg|20px]] Argentińskô
| RD2-team14=[[File:Flag of Egypt.svg|20px]] Egipt
| RD2-score13=
| RD2-score14=
| RD2-text08=7 lëpińca – [[BC Place Stadium|Vancouver]]
| RD2-team15=[[File:Flag of Switzerland.svg|20px]] Szwajcarskô
| RD2-team16=[[File:Flag of Colombia.svg|20px]] Kòlumbiô
| RD2-score15=
| RD2-score16=
| RD3-text01=9 lëpińca – [[Gillette Stadium|Foxborough]]
| RD3-team01=[[File:Flag of Morocco.svg|20px]] Marokò
| RD3-team02=[[File:Flag of France.svg|20px]] Francëskô
| RD3-score01=
| RD3-score02=
| RD3-text02=10 lëpińca – [[SoFi Stadium|Inglewood]]
| RD3-team03=Dobiwca meczu 93
| RD3-team04=Dobiwca meczu 94
| RD3-score03=
| RD3-score04=
| RD3-text03=11 lëpińca – [[Hard Rock Stadium|Miami Gardens]]
| RD3-team05=[[File:Flag of Norway.svg|20px]] Norweskô
| RD3-team06=Dobiwca meczu 92
| RD3-score05=
| RD3-score06=
| RD3-text04=12 lëpińca – [[Arrowhead Stadium|Kansas City]]
| RD3-team07=Dobiwca meczu 95
| RD3-team08=Dobiwca meczu 96
| RD3-score07=
| RD3-score08=
| RD4-text01=14 lëpińca – [[AT&T Stadium|Arlington]]
| RD4-team01=Dobiwca meczu 97
| RD4-team02=Dobiwca meczu 98
| RD4-score01=
| RD4-score02=
| RD4-text02=15 lëpińca – [[Mercedes-Benz Stadium|Atlanta]]
| RD4-team03=Dobiwca meczu 99
| RD4-team04=Dobiwca meczu 100
| RD4-score03=
| RD4-score04=
| RD5-text01=19 lëpińca – [[MetLife Stadium|East Rutherford]]
| RD5-team01=Dobiwca meczu 101
| RD5-team02=Dobiwca meczu 102
| RD5-score01=
| RD5-score02=
| RD5b-text01=18 lëpińca – [[Hard Rock Stadium|Miami Gardens]]
| RD5b-team01=Przerżnóny meczu 101
| RD5b-team02=Przerżnóny meczu 102
| RD5b-score01=
| RD5b-score02=
}}
=== 1/16 finału ===
{{fùsbalowi mecz
| etap = Mecz 73
| data = 28 czerwińca 2026, 21:00
| stadión = SoFi Stadium, Los Angeles
| druzyna1 = Pôłniowô Afrika
| fana1 = RSA
| wënik1 = 0
| druzyna2 = Kanada
| fana2 = Canada
| wënik2 = 1
| gòle2 = Eustáquio 90+2
| òbzérôczë = 69 237
| sãdza = João Pinheiro
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 74
| data = 29 czerwińca 2026, 19:00
| stadión = NRG Stadium, Houston
| druzyna1 = Brazylskô
| fana1 = Brazil
| wënik1 = 2
| druzyna2 = Japòńskô
| fana2 = Japan
| wënik2 = 1
| gòle1 = Casemiro 56’, Martinelli 90+5
| gòle2 = Sano 29’
| òbzérôczë = 68 777
| sãdza = Maurizio Mariani
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 75
| data = 29 czerwińca 2026, 22:30
| stadión = Gillette Stadium, Boston
| druzyna1 = Niemieckô
| fana1 = Germany
| wënik1 = 1
| druzyna2 = Paragwaj
| fana2 = Paraguay
| wënik2 = 1
| karné = 3-4
| gòle1 = Havertz 54’
| gòle2 = Enciso 42’
| òbzérôczë = 63 945
| sãdza = Jalal Jayed
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 76
| data = 30 czerwińca 2026, 03:00
| stadión = Estadio BBVA, Monterrey
| druzyna1 = Niderlandzkô
| fana1 = Netherlands
| wënik1 = 1
| gòle1 = Gakpo 72’
| druzyna2 = Marokò
| fana2 = Morocco
| wënik2 = 1
| karné = 2-3
| gòle2 = Diop 90+1
| òbzérôczë = 68 777
| sãdza = Wilton Sampaio
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 77
| data = 30 czerwińca 2026, 19:00
| stadión = AT&T Stadium, Arlington
| druzyna1 = Ùbrzég Słoniowëch Kłów
| fana1 = Ivory Coast
| wënik1 = 1
| gòle1 = Diallo 74’
| druzyna2 = Norweskô
| fana2 = Norway
| wënik2 = 2
| gòle2 = Nusa 39’, Haaland 86’
| òbzérôczë = 69 665
| sãdza = Jesús Valenzuela
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 78
| data = 30 czerwińca 2026, 23:00
| stadión = MetLife Stadium, East Rutherford
| druzyna1 = Francëskô
| fana1 = France
| wënik1 = 3
| druzyna2 = Szwedzkô
| fana2 = Sweden
| wënik2 = 0
| gòle1 = Mbappé 45’, 74’; Barcola 53’
| òbzérôczë = 80 663
| sãdza = Danny Makkelie
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 79
| data = 1 lëpińca 2026, 03:00
| stadión = Estadio Azteca, Meksyk
| druzyna1 = Meksyk
| fana1 = Mexico
| wënik1 = 2
| druzyna2 = Ekwador
| fana2 = Ecuador
| wënik2 = 0
| gòle1 = Quiñones 22’, Jiménez 31’
| òbzérôczë = 80 824
| sãdza = Slavko Vinčić
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 80
| data = 1 lëpińca 2026, 18:00
| stadión = Mercedes-Benz Stadium, Atlanta
| druzyna1 = Anielskô
| fana1 = England
| wënik1 = 2
| druzyna2 = Demòkraticznô Repùblika Kònga
| fana2 = Democratic Republic of the Congo
| wënik2 = 1
| gòle1 = Kane 75’, 86’
| gòle2 = Cipenga 7’
| òbzérôczë = 68 239
| sãdza = Adham Makhadmeh
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 81
| data = 1 lëpińca 2026, 22:00
| stadión = Lumen Field, Seattle
| druzyna1 = Belgiô
| fana1 = Belgium
| wënik1 = 3
| gòle1 = Lukaku 86’; Tielemans 89’, 120+5'
| druzyna2 = Senegal
| fana2 = Senegal
| wënik2 = 2
| gòle2 = Diarra 24’, Sarr 51’
| òbzérôczë = 66 925
| sãdza = Said Martínez
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 82
| data = 2 lëpińca 2026, 02:00
| stadión = Levi’s Stadium, Santa Clara
| druzyna1 = USA
| fana1 = USA
| wënik1 = 2
| gòle1 = Balogun 78’, Tillman 82'
| druzyna2 = Bòsniô a Hercegòwina
| fana2 = Bosnia and Herzegovina
| wënik2 = 0
| òbzérôczë = 68 827
| sãdza = Raphael Claus
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 83
| data = 2 lëpińca 2026, 22:00
| stadión = SoFi Stadium, Los Angeles
| druzyna1 = Szpańskô
| fana1 = Spain
| wënik1 = 3
| gòle1 = Oyarzabal 36', 89'; Porro 66'
| druzyna2 = Aùstriackô
| fana2 = Austria
| wënik2 = 0
| òbzérôczë = 70 492
| sãdza = Glenn Nyberg
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 84
| data = 2 lëpińca 2026, 18:00
| stadión = GEHA Field at Arrowhead Stadium, Kansas City
| druzyna1 = Pòrtugalskô
| fana1 = Portugal
| wënik1 = 2
| gòle1 = Cristiano Ronaldo 68' (k.), Gonçalo Ramos 90+4'
| druzyna2 = Chòrwackô
| fana2 = Croatia
| wënik2 = 1
| gòle2 = Perišić 41'
| òbzérôczë = 43 036
| sãdza = Espen Eskas
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 85
| data = 3 lëpińca 2026, 02:00
| stadión = BC Place, Vancouver
| druzyna1 = Szwajcarskô
| fana1 = Switzerland
| wënik1 = 2
| gòle1 = Embolo 10'; Ndoye 46'
| druzyna2 = Algerskô
| fana2 = Algeria
| wënik2 = 0
| òbzérôczë = 52 497
| sãdza = Yael Falcon Perez
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 86
| data = 3 lëpińca 2026, 20:00
| stadión = AT&T Stadium, Arlington
| druzyna1 = Aùstraliô
| fana1 = Australia
| wënik1 = 1
| druzyna2 = Egipt
| fana2 = Egypt
| wënik2 = 1
| karné = 2-4
| gòle1 = Hany 55'
| gòle2 = Ashour 13'
| òbzérôczë = 70 244
| sãdza = Gustavo Tejera
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 87
| data = 3 lëpińca 2026, 22:00
| stadión = Hard Rock Stadium, Miami Gardens
| druzyna1 = Argentińskô
| fana1 = Argentina
| wënik1 = 3
| gòle1 = Messi 29'; Lautaro Martínez 92'; Diney 111'
| druzyna2 = Cabo Verde
| fana2 = Cape Verde
| wënik2 = 2
| gòle2 = D. Duarte 59'; Lopes Cabral 103'
| òbzérôczë = 64 478
| sãdza = Drew Fischer
}}
<br>
-------
{{fùsbalowi mecz
| etap = Mecz 88
| data = 4 lëpińca 2026, 02:30
| stadión = GEHA Field at Arrowhead Stadium, Kansas City
| druzyna1 = Kòlumbiô
| fana1 = Colombia
| wënik1 = 1
| gòle1 = J. Arias 14'
| druzyna2 = Ghana
| fana2 = Ghana
| wënik2 = 0
| òbzérôczë = 69 045
| sãdza = Clément Turpin
}}
=== 1/8 finału ===
{{fùsbalowi mecz
| etap = Mecz 89
| data = 4 lëpińca 2026, 13:00
| stadión = NRG Stadium, Houston
| druzyna1 = Kanada
| fana1 = Canada
| wënik1 = 0
| druzyna2 = Marokò
| fana2 = Morocco
| wënik2 = 3
| gòle2 = Ounahi 50’, 82’; Rahimi 90+8’
| òbzérôczë = 68 777
| sãdza = Michael Oliver
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 90
| data = 4 lëpińca 2026, 21:00
| stadión = MetLife Stadium, East Rutherford
| druzyna1 = Paragwaj
| fana1 = Paraguay
| wënik1 = 0
| druzyna2 = Francëskô
| fana2 = France
| wënik2 = 1
| gòle2 = Mbappé 70' (k.)
| òbzérôczë = 68 324
| sãdza = Ilgiz Tantashev
}}
<br>
----
{{fùsbalowi mecz
| etap = Mecz 91
| data = 5 lëpińca 2026, 22:00
| stadión = NRG Stadium, Houston
| druzyna1 = Brazylskô
| fana1 = Brazil
| wënik1 = 1
| gòle1 = Neymar 90+10'
| druzyna2 = Norweskô
| fana2 = Norway
| wënik2 = 2
| gòle2 = Haaland 79', 90'
| òbzérôczë = 80 663
| sãdza = Ismail Elfath
}}
<br>
'''Bôczënk:''' jinfòrmacje zrzeszoné z pòstãpnyma zeńdzeniama mdą aktualizowóné przez całi czas.
== Finał ==
Finał òdbëwa sã [[19 lëpińca]] 2026 rokù na stadionie ''MetLife Stadium'' w East Rutherford (aglomeracjô [[Nowi Jork|Nowégò Jorkù]]).
== Òbaczë téż ==
{{#categorytree:Nożnô bala|hideroot=on|mode=pages}}
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
{{Kòntrola}}
[[Kategòrëjô:Nożnô bala]]
32yi0coyysfnkar6ftbyxyqcji7vm49
Kania
0
13166
204481
203263
2026-07-05T19:32:32Z
Rabùszniczka
19710
Usunięto przekierowanie do [[Milvus]]
204481
wikitext
text/x-wiki
'''Kanie''' abò '''kùlindżi''' (''Milvus'') – pòzwa na ôrt drapiérzëch ptôchów z rodzëznë [[Accipitridae|jastrzibòwatëch]] (Accipitridae), jakô w kaszëbsczim jãzekù òdnosy sã do rodzajów [[Milvus]] (do jaczégò nôleżą [[lësatô kania]], [[czôrnô kania]], [[egpiskô kania]]), ''[[Haliastur]], [[Ictinia]] (''kanialuczi'').''<ref>Ramułt, Stefan, ''Słownik języka pomorskiego, czyli kaszubskiego, s.112''</ref>
rjddl5k0e4rb5h6y193q93fry49zikt
204503
204481
2026-07-06T11:23:22Z
Iketsi
3254
[[Kategòrëjô:Jastrzibowaté]]
204503
wikitext
text/x-wiki
'''Kanie''' abò '''kùlindżi''' (''Milvus'') – pòzwa na ôrt drapiérzëch ptôchów z rodzëznë [[Accipitridae|jastrzibòwatëch]] (Accipitridae), jakô w kaszëbsczim jãzekù òdnosy sã do rodzajów [[Milvus]] (do jaczégò nôleżą [[lësatô kania]], [[czôrnô kania]], [[egpiskô kania]]), ''[[Haliastur]], [[Ictinia]] (''kanialuczi'').''<ref>Ramułt, Stefan, ''Słownik języka pomorskiego, czyli kaszubskiego, s.112''</ref>
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Jastrzibowaté]]
qu6t7d2m8g38rti6vepjnfj8xqyorbu
204504
204503
2026-07-06T11:25:12Z
Iketsi
3254
== Òbaczë téż == {{#categorytree:Jastrzibowaté|hideroot=on|mode=pages}}
204504
wikitext
text/x-wiki
'''Kanie''' abò '''kùlindżi''' (''Milvus'') – pòzwa na ôrt drapiérzëch ptôchów z rodzëznë [[Accipitridae|jastrzibòwatëch]] (Accipitridae), jakô w kaszëbsczim jãzekù òdnosy sã do rodzajów [[Milvus]] (do jaczégò nôleżą [[lësatô kania]], [[czôrnô kania]], [[egpiskô kania]]), ''[[Haliastur]], [[Ictinia]] (''kanialuczi'').''<ref>Ramułt, Stefan, ''Słownik języka pomorskiego, czyli kaszubskiego, s.112''</ref>
== Òbaczë téż ==
{{#categorytree:Jastrzibowaté|hideroot=on|mode=pages}}
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Jastrzibowaté]]
q3en9x4x4296uo7me5c01vnfjhba1ez
Głos Kaszubski
0
13238
204508
204341
2026-07-06T11:49:13Z
Iketsi
3254
Òbrôzk
204508
wikitext
text/x-wiki
{{Nôpiérwi pò kaszëbskù}}
[[Òbrôzk:NDIGCZAS097591 1930 001.pdf|mały|Głos Kaszubski – R. 1, 1930, nr 1]]
'''Głos Kaszubski''' – cządnik.
== Òbaczë téż ==
{{#categorytree:Kaszëbsczé cządniczi|hideroot=on|mode=pages}}
== Przëpisë ==
{{Przëpisë}}
== Bùtnowé lënczi ==
{{Commons}}
* https://jbc.bj.uj.edu.pl/dlibra/publication/1005385
;Dom i Rodzina
* https://jbc.bj.uj.edu.pl/dlibra/publication/1050521
{{Ùzémk artikla}}
{{Kòntrola}}
[[Kategòrëjô:Kaszëbsczé cządniczi]]
fco75dlwtc3qtd8bmedxz3fnqs2otzc
Moduł:OSM Location map
828
13245
204448
2026-07-05T14:28:58Z
HaxelKaszëba
17492
Ùsôdzonô nowô starna "require('strict') local delink=require('Module:Delink').delink local getArgs = require('Module:Arguments').getArgs local p = {} local maplist={} local sgNames={} local highlightOption=false local highlightNum local visibleLinks local geoTotalPop = 0 local captionTable = '' local captionTableText = {} -- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations, -- dots, shapes, lines and other ways to customise the…"
204448
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
local geoTotalPop = 0
local captionTable = ''
local captionTableText = {}
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it may at some point be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
-- -------------- the following text variables are used within the template, and can be translated as required ---------------
-- -------------- you may wish to retain the English version as a comment, to keep track of the original phrase --------------
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext = 'Click for interactive fullscreen map with links to nearby articles'
local toggletext = 'List of numbered map items:'
local termsOfUse = 'Maps: terms of use'
local aboutOSM = 'About OpenStreetMaps'
local yards = 'yds'
local miles = 'miles'
local metres = 'm'
local km = 'km'
local sqkm = 'sq.km'
local year = 'year'
local notFound = 'not found'
local totalItems = 'Total'
local sumOfDatapoints = 'sum of datapoints'
local numberCol = 'No.' -- abbreviated for top of column of code numbers
local wikidataLink = 'Wikidata page'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.semicircle = "M0,5 C0,18.3,20,18.3,20,5 L 0,5z"
pathshape.arrowhead = "M 10,10.2 l 10,2.8 -2,-3 2,-3 -10,2.8 -0.2,0.2z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]') )
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
local t= string.find(label,'wikidata') -- exclude wikidata from tooltip line
if t then
label=string.sub(label,1,t-2) -- don't show wikidata value in tooltip
if string.find(link,'wikidata') then link = '' end -- don't link on click to wikidata (if there was not another link)
end
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r,test)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x,y
x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.075*(math.abs(t.latbase)/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat,magVal)
if magVal and magVal>1 and magVal <=2 then zoom=zoom+(magVal-1) end
local dist=(156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom))/17
local y
if dist < 1 then
y=(round(dist*10,1))
return tostring(y*100)..metres, tostring(round(y*109,0))..yards
elseif dist <18 then
y=(round(dist,0))
return tostring(y)..km, tostring(round(y*0.621371,1))..miles
elseif dist <500 then
y=round(dist/10)
return tostring(y*10)..km, tostring(round(y*6.21371,0))..miles
else
y=round(dist/100)
return tostring(y*100)..km, tostring(round(y*62.1371,0))..miles
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function htmlTableLine (htmlTable,h1,h2,h3,h4,h5,type)
if type == 'header' then
if (h4 or '') ~= 'no-year' then h4='<th class="unsortable">'..h4 or ''..'</th>' else h4 = '' end
if (h5 or '') ~= '' then h5='<th class="unsortable">'..h5..'</th>' else h5 = '' end
return htmlTable..'<tr><th>'..(h1 or '')..'</th><th>'..(h2 or '')..'</th><th>'..(h3 or '')..'</th>'..h4..h5..'</tr>'
else
if (h4 or '') ~= 'no-year' then h4='<td>'..h4 or ''..'</td>' else h4 = '' end
if (h5 or '') ~= '' then h5='<td>'..h5..'</td>' else h5 = '' end
return htmlTable..'<tr><td>'..(h1 or '')..'</td><td>'..(h2 or '')..'</td><td>'..(h3 or '')..'</td>'..h4..h5..'</tr>'
end
end
local function formatInt(number)
if number == 0 then return notFound end
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
int = int:reverse():gsub("(%d%d%d)", "%1,")
return minus .. int:reverse():gsub("^,", "") .. fraction
end
local function convertToSqKm (value,punit)
if string.find(punit, 'Q35852') then value = value / 100 -- convert to sq km if it's hectares
elseif string.find(punit, 'Q232291') then value = value /(1/2.59) -- or this if it's square miles
elseif string.find(punit, 'Q81292') then value = value / 247.1 -- or this if it's acres
end
return value
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
local qvalue=''
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
-- Assign autovalues if a wikidata Qvalue has been supplied
-- some preliminary documentation
-- geo-data-type= default is P1082 (population)
-- other options might include number of households (P1538) and per capita income (P10622) or land area (P2046)
-- geo-data-list= this is a comma-separated list of Q values, for a manually generated list that doesn't require nymbered parameters
-- geo-scalefactor = can enlarge or shrink all the dots in proportion, depending an what the map needs
-- geo-region = needs to be a Qvalue of the larger administrative region that the settlements/sub-regions are in
-- geo-sub-region needs to be a Pvalue, but will default to P150 (some pages use 'has parts' = P527)
-- geo-data3= etc numbered Qvalue item, if used in conjunction with other shape/label parameters
-- shapeD= sets default shape, which migh be n-circle to include numbers.
-- geo-number-size= will give a fixed size for shape numbers. (defaults to 12 when using proportional dots)
-- geo-boundaries= if set to 1 will add any Qvalues for the sub-regions to 'map-data-light'.
-- show-geo-dots= to use proportional dots. Will be fixed size if 0. (set mark-sizeD=0 to have no dot at all, eg to show labels or boundaries)
-- find-geo-dots=1 or 0 (needs to also include geo-region)
-- label-posD = gets additional options: [off] gives no label, [on] will position lower centre,
-- [on+] includes numeric values beside/below label, [on^] will always put it below. [on-] always puts values beside
if args['geo-data'..argval] then -- and (args['mark-title'..argval] or '') ~= 'none' then
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
if not args['geo-number-size'] then args['geo-number-size']=12 end
if not args['label-posD'] then args['label-posD'] = 'on' end
qvalue=args['geo-data'..argval]
-- total poulation is a Qvalue wikidata page entry, or add up all the geo-data values or use a random 10000
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local thisPop=0
local areatxt=''
local yearVal=''
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
thisPop=tonumber(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.amount) or 0
if pvalue == 'P2046' then -- area, so convert from ha to sq km if needed (/100)
thisPop = convertToSqKm(thisPop, mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit)
end
if mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers and mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585 then
yearVal = mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal then yearVal = yearVal:sub(2,5) end -- wikidata reports an entire date and time, so extract the year
end
else
debugmsg(mw.addWarning(mw.wikibase.getLabel(qvalue)..' ('..qvalue..') has no recorded '..plabel))
end
local scalefactor=args['geo-scalefactor'] or 1
if argval=='1' and args['mark-size1'] and not args['mark-sizeD'] then args['mark-sizeD'] = args['mark-size1'] end
if argval=='1' and args['label-pos1'] and not args['label-posD'] then args['label-posD'] = args['label-pos1'] end
if not args['mark-size'..argval] then
if args['show-geo-dots'] == '0' then
args['mark-size'..argval] = args['mark-sizeD'] or '1' -- just use a 'point' for the label to attach to
else
if thisPop>0 then
args['mark-size'..argval] = round(math.sqrt(thisPop/geoTotalPop*100) * 8 * scalefactor,0)
else
args['mark-size'..argval] = args['mark-sizeD'] or '1'
args['shape-outline'..argval]=args['shape-outline'..argval] or args['shape-outlineH']
end
end
end
local posTemp=string.lower(multiCheck(args,'label-pos',argval,'on'))
args['label-pos'..argval] = posTemp
posTemp=splitItem(posTemp,2)[1]
if pvalue == 'P2046' then
thisPop = round(thisPop,2)
areatxt=sqkm..areatxt -- round decimals if sq km area
elseif pvalue == 'P10622' then
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
local punit=(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit or ''):match("Q.*") -- extract Q value
if punit == 'Q4917' then areatxt=' US$'
elseif punit == 'Q4916' then areatxt=' euro'
elseif punit == 'Q25224' then areatxt=' £GBP'
else areatxt=mw.wikibase.getLabel(punit)..areatxt
end
end
end
if posTemp=='off' then
else
if not args['label'..argval] then args['label'..argval] = mw.wikibase.getLabel(qvalue) end
local tline=posTemp:find("[%+%^%-]")
if tline then
local tval=posTemp:sub(tline,tline)
local eline
posTemp=posTemp:sub(1,tline-1)
if posTemp == 'left' or posTemp == 'right' or string.find(posTemp,'east') or string.find(posTemp,'west') then
if tval == '^' then eline = '^' else eline = ' ' end -- only add line break if 'forced', used for left/right
elseif tval == '+' or tval == '^' then eline = '^' -- add linebreak unless 'forced' not to
else eline = ' ' -- must be a dash, so no line break
end
args['label'..argval] = args['label'..argval]..eline..'('..formatInt(thisPop)..')'
args['label-pos'..argval] = posTemp..args['label-pos'..argval]:sub(tline+1,#args['label-pos'..argval])
end
end
if posTemp=='on' then
if argval=='1' and (not args.ldyD) then args.ldyD = args['ldy'..argval] or '0' end
if not args['ldy'..argval] then args['ldy'..argval] = args.ldyD end -- ensure ldy for that dot is added to the position
args['label-pos'..argval]='center'
local _,br = string.gsub(args['label'..argval],'%^','')
local item=splitItem(args['label-size'..argval],2)
args['ldy'..argval] = (args['ldy'..argval] or 0) + ((item[1] or 11) * ((br+1)*0.6)) + ((args['geo-number-size'] or 12) / 2)
end
local nt=''
if args['mark-title'..argval] == 'nolist' then nt='nolist,' end
if not args['mark-title'..argval] or args['mark-title'..argval] == '' or args['mark-title'..argval] == 'nolist' then
if mw.wikibase.getSitelink(qvalue) then
args['mark-title'..argval] = nt..'[['..mw.wikibase.getSitelink(qvalue)..']]'
else
args['mark-title'..argval] = nt..mw.wikibase.getLabel(qvalue)
end
end -- If mark-title is not set locally then attempt Sitelink value, or use label
if thisPop == 0 then areatxt='' end
captionTableText[argval]={}
captionTableText[argval][1]= args['mark-title'..argval]
captionTableText[argval][2]= formatInt(thisPop)
if maplist.width >= 280 then captionTableText[argval][3]= yearVal else captionTableText[argval][3]='no-year' end
if args['show-q-values'] == '1' then
captionTableText[argval][4]='<small>[[wikidata:'..qvalue..'|'..qvalue..']]</small>'
else
local nbsp = ' '
if maplist.width < 230 then nbsp = ' ' end
captionTableText[argval][4]=''
if captionTableText[argval][3] == 'no-year' then
captionTableText[argval][2] = captionTableText[argval][2]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
else
captionTableText[argval][3] = captionTableText[argval][3]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
end
end
if args['mark-title'..argval] == 'none' then
-- no title
else
args['mark-title'..argval] = args['mark-title'..argval]..' ('..plabel..' '..formatInt(thisPop)..areatxt..')'
end
if not args['mark-description'..argval] then args['mark-description'..argval] = (mw.wikibase.getDescription(qvalue) or '')..'<br>' end
if (not args['mark-image'..argval]) and (mw.wikibase.getBestStatements(qvalue,'P18')[1]) then
args['mark-image'..argval] = mw.wikibase.getBestStatements(qvalue,'P18')[1].mainsnak.datavalue.value
end
item = multiCheck(args,'shape',argval,'circle') --default to circle if geo-data
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
else
item = multiCheck(args,'shape',argval,'image')
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
end
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,(shapeResult[nval].shapeSize or '14px')) -- add geo-data as fallback if already assigned
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
if args['shape-angle'..argval] then shapeResult[nval].shapeAngleArg=true else shapeResult[nval].shapeAngleArg=false end
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
shapeResult[nval].textAT=args['label-attribute'..argval] or ''
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
if args['label-angle'..argval] then shapeResult[nval].textNGarg=true else shapeResult[nval].textNGarg=false end
--sgd=dotTag attributes
shapeResult[nval].tagSize=(args['geo-number-size'] or tostring(shapeHeight*1.5))..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
if itemTab[1] == nil or itemTab[2] == nil then
debugmsg(mw.addWarning('Unable to read coordinates for mark-coord'..argval))
else
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
end
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
if string.find(item,'nolist') then dotItem.param1='nolist,' item = string.sub(args['mark-title'..argval],8) or args['label'..argval] end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
-- debugmsg('dlink for '..item..' is '..dotItem.dlink)
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
-- debugmsg(dotItem.code..' = '..dotItem.lat..','..dotItem.lon..': '..dotItem.labelText..' - '..dotItem.title..', '..shapeResult[nval].shapeSize)
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['geo-data'..x] and (not args['mark-coord'..x]) and (not args['mark-lat'..x]) then
-- get lat and lon values from wikidata and put them into the args.lat and args.lon for use wherever needed
if (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1]) then
args['mark-lat'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.latitude) or '0'
args['mark-lon'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.longitude) or '0'
local p=args['geo-data-type'] or 'P1082'
if mw.wikibase.getBestStatements(args['geo-data'..x],p)[1] then
local n = tonumber(mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.amount) or 0
if p == 'P2046' then -- area, so convert to sq km if needed (/100)
n = convertToSqKm( n, mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.unit)
end
geoTotalPop = geoTotalPop + n
end
else debugmsg(mw.addWarning('could not find coords for '..mw.wikibase.getLabel(args['geo-data'..x])..', '..args['geo-data'..x]))
end
end
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form (needs a second table, seemingly)
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
local unitConvert=1
local areaUnits=''
if args['geo-region'] and (args['find-geo-dots'] or '1') ~= '0' then
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
local subhead='' -- Assemble the subhead content, a line of text about the region as a whole
if region and mw.wikibase.getBestStatements(region,pvalue)[1] then
local punit=mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.unit:match("Q.*") -- extract Q value
if pvalue == 'P10622' then -- add other currency-based pvalues here
areaUnits=mw.wikibase.getLabel(punit)
end
if punit == 'Q35852' then unitConvert=100 areaUnits = ' '..sqkm -- if its hectares, convert to sq km
elseif punit == 'Q232291' then unitConvert = (1/2.59) areaUnits = ' '..sqkm -- or this if it's square miles
elseif punit == 'Q81292' then unitConvert = 247.1 areaUnits = ' '..sqkm -- or this if it's acres
elseif punit == 'Q712226' then unitConvert = 1 areaUnits = ' '..sqkm -- or this if it's already sq km
elseif punit == 'Q4917' then areaUnits=' US$' -- replace long version with some shortform currencies ()
elseif punit == 'Q4916' then areaUnits=' euro'
elseif punit == 'Q25224' then areaUnits=' £GBP'
end
-- if pvalue == 'P2046' then
-- may need to add other instances of sq km so add in other P-items here if they need units showing
-- end
geoTotalPop=(mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.amount) / unitConvert
local yearVal
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers then
yearVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal and yearVal:sub(1,1) == '+' then yearVal = yearVal:sub(2,5)
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459 and mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1] then
local methodVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1].datavalue.value.id
if methodVal then yearVal=yearVal..' '..mw.wikibase.getLabel(methodVal) end
end
yearVal = ' ('..yearVal..')'
else yearVal =''
end
end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ([[wikidata:'..region..']]) = '..formatInt(round(geoTotalPop),1)..areaUnits..yearVal
else
if pvalue == 'P2046' then areaUnits = sqkm end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ('..sumOfDatapoints..') = '..formatInt(round(geoTotalPop),1)..areaUnits
end
if maplist.width < 350 and #plabel > 4 then plabel=string.sub((plabel or ' '),1,3)..'.' end
if maplist.width < 330 then numberCol = ' ' end
if maplist.width < 280 then year = 'no-year' end
captionTable = subhead
if (args['show-q-values'] or '') ~= '1' then
captionTable = captionTable..'<div style="float:right;"><small> (WD = '..wikidataLink..')</small></div><br>'
wikidataLink=''
end
captionTable = captionTable..'<table class="wikitable plainrowheaders sortable mw-collapsible mw-collapsed" style="width: 100%;"><caption>'..(args.toggletext or toggletext)..' </caption>'
captionTable = htmlTableLine(captionTable,numberCol,mw.wikibase.getLabel(region),plabel..' '..areaUnits, year, wikidataLink,'header')
end
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double,shape)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
local tw
if double <= 1 then
tw = tonumber(string.match(oWid,"[%.%d]+"))/2 -- shift centre by half the line width
else
tw = tonumber(string.match(oWid,"[%.%d]+")) + (double/2) - 0.5 -- shift centre by a line width + half the gap
end
-- debugmsg(oWid..'+'..double..'/2='..tw)
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2) ).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) - tw).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
local lineAngle=math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi
-- if x1 == x2 then lineAngle=90 end
table.insert(result,"transform: rotate("..lineAngle.."deg);\"></div>")
-- debugmsg('text angle started as '..shape.textNG..' and lineAngle = '..lineAngle)
if (lineAngle < -90) or (lineAngle >90) then lineAngle=lineAngle-180 end
if shape and not shape.textNGarg then shape.textNG=tostring(round(lineAngle,0))..'deg' end -- Put in line angle for a default text angle as well
-- if (y2 >= y1) and (x2 <= x1) then lineAngle=lineAngle-180 debugmsg('arrow angle corrected') end
if x2 < x1 then lineAngle=lineAngle-180 end
if shape and not shape.shapeAngleArg and shape.shape == 'arrowhead' then shape.shapeAngle=tostring(round(lineAngle,0))..'deg' end -- line angle for arrowhead
-- debugmsg('text angle is now '..shape.textNG..'with arrow at '..lineAngle)
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if string.find(shape.shape, 'svgpath,') and #shape.shape > 9 then
pathshape.svgpath=string.sub(shape.shape,9)
shape.shape='svgpath'
end
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if shape.shape == 'arrowhead' then -- enlarge arrowheads, as they only go to the middle of the notional shape-size
w=w*2.5
h=h*2.5
end
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result," scale("..tostring(w/16)..", "..tostring(h/16)..")")
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy+2 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,rndg=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = (w/2)+1,(h/2)+1
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
-- debugmsg('r-(w/2) > widthzone r-w/2='..tostring(r-(w/2))..', widthzone='..widthzone..', label='..(row.labelText or '')..', postype='..(row.posType or ''))
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
-- debugmsg('n-line for '..row.code..':'..tostring(row.gridx+1)..', '..tostring(row.gridy-1)..', '..tostring(row.gridx+row.dx+offsetx)..', '..tostring(row.gridy+row.dy+offsety)..', with width '..tostring(shape.textEW)..'px '..(shape.textES or 'no style')..' and color '..shape.textEC)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap,shape)
-- debugmsg('mark-line line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..(row.mlStyle or 'no style')..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
if color[2] then
propertyTable['stroke-opacity'] = tonumber(color[2] or '20')/100
end
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] and args['map-data-light'] ~= '' then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.4 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
comma=', '
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
local function getQset (qValue,pValue,argNumber,args)
local result={}
local sortType=string.lower(args['geo-list-sort'] or 'a')
local testValue='0'
if sortType == 'alpha' or sortType == 'a' or sortType == 'ah' or sortType == 'al' then
testValue = mw.wikibase.getLabel(qValue)
elseif sortType == 'value' or sortType == 'v' or sortType == 'vh' or sortType == 'vl'then
if qValue==nil or pValue == nil then debugmsg(mw.addWarning((qValue or 'nil')..' Qnumber for '..(pValue or 'nil')))
else
if mw.wikibase.getBestStatements(qValue,pValue)[1] then
testValue = mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.amount or '0'
if pValue == 'P2046' then
testValue = convertToSqKm(testValue, mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.unit)
end
end
end
elseif sortType == 'north' or sortType == 'n' or sortType == 'nh' or sortType == 'nl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.latitude or 0
end
elseif sortType == 'west' or sortType == 'w' or sortType == 'wh' or sortType == 'wl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.longitude or 0
end
end
result[1]=testValue
result[2]=argNumber
return result
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb floatleft">')
else table.insert(result,'<div class="thumb floatright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
--add any boundary Qvalues to map-data-light if requested
if args['geo-region'] and (args['geo-boundaries'] == '1') then
local qmaps=args['map-data-light'] or ''
if qmaps ~='' and string.sub(qmaps,#qmaps) ~= ',' then qmaps=qmaps..',' end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
if mw.wikibase.entityExists(qtemp) then
qmaps=qmaps..qtemp..',' -- see if there is a geocode boundary Qvalue to add
else debugmsg(mw.addWarning('unable to add geocode value for '..mw.wikibase.getLabel..','..qtemp))
end
end
ct=ct+1
end
end
if qmaps and qmaps ~= '' then -- add back the created/expanded list of boundary Qvalues, and remove trailing comma
args['map-data-light'] = string.sub(qmaps,1,#qmaps-1)
end
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
local qset={}
for indx,val in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') or string.match(indx,'geo%-data[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
if string.match(indx,'geo%-data[%d]+') then
local argnum=string.sub(indx,9,#indx) or ''
if args['mark-title'..argnum] == 'none' or (string.find(args['mark-title'..argnum] or 'x','nolist')== 1) then
-- debugmsg('sort values ignored '..argnum..', '..args['mark-title'..argnum])
else
qset[val]=getQset (val, (args['geo-data-type'] or 'P1082'), argnum, args)
end
end
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
local qlist=args['geo-data-list']
if qlist then
if string.sub(qlist,#qlist) ~= ',' then qlist=qlist..',' end
else
qlist=''
end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if (args['find-geo-dots'] or '1') ~= '0' and geoRegion and mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(adminRegion,adminSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
qlist=qlist..qtemp..',' -- add subregion Qvalue to the list
end
ct=ct+1
end
end
if qlist and #qlist>1 then -- create a table from the csv list
local _, c = qlist:gsub(",","")
local qtab=splitItem(qlist,c)
for xd,yd in pairs(qtab) do
yd=string.upper(yd)
if qset[yd] == nil and mw.wikibase.isValidEntityId(yd) then
dotmax=dotmax+1
qset[yd]=getQset (yd, (args['geo-data-type'] or 'P1082'), tostring(dotmax), args)
args['geo-data'..tostring(dotmax)] = yd
-- debugmsg(dotmax..' = '..(string.upper(yd) or 'Q Not found')..': '..(mw.wikibase.getLabel(yd) or 'label Not found')..': '..(mw.wikibase.getSitelink(yd) or 'link Not found'))
elseif not mw.wikibase.isValidEntityId(yd) then
debugmsg(mw.addWarning(yd..' is not a [[wikidata]] page. geo-data-list needs comma-separated Qvalues' ))
else debugmsg(mw.addWarning(yd..' is duplicate'))
end
end
end
-- qset contains set of Qvalues and can be from wikidata geo-region, list of geo-data-list Q values or individual geo-data1= etc
local ts = {} -- table to hold the keys
local useQset=false
for ky, val in pairs(qset) do
table.insert(ts, {key = ky, value = val[1], num=val[2]})
useQset=true
end
if useQset then
local order = string.lower(args['geo-list-sort'] or 'a') -- sort the list from previously stored sortkey, either Lowest or Highest first
if string.find(',al,vl,nl,wl,west,w,alpha,a,' , ','..order..',') then
table.sort(ts, function (a,b) return lessthan(a.value, b.value) end)
else
table.sort(ts, function (a,b) return morethan(a.value, b.value) end)
end
for ky,val in ipairs(ts) do -- assign values from sorted list to numbered parameter
args['numbered'..val.num] = ky -- The numbered parameter becomes the value for each dot that has a geo Qvalue, from manual, list or wiki sources.
end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) or args['geo-data'..x] then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,round(dotitem.lat,5),round(dotitem.lon,5),fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
if captionTable ~= '' and (not args['auto-caption']) then args['auto-caption']='on' end --default to include list if using geo values
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if negativeAnswer[autoOff] then captionTable='' end
if args.caption or (not negativeAnswer[autoOff]) then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then
table.insert(result,args.caption)
if captionTable ~= '' then table.insert(result,'<hr>') end
end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
if captionTable == '' then
capchk=true
else -- add the next line of content to the html table
capchk=false
end
end
end
if captionTable ~= '' then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
for k,v in pairs(captionList) do
local nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
local ngrp=v.value.group
if captionTableText[ngrp] then
-- debugmsg('table text for'..v.value.dotTag..', '..captionTableText[v.value.group][1]..', '..captionTableText[v.value.group][2]..', '..captionTableText[v.value.group][3]..' ('..captionTableText[v.value.group][4]..')')
local c1,c2
c1,c2=checkColors(shapeList[ngrp].shapeColor)
local cDot='<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;'
cDot=cDot..'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div>'
captionTable = htmlTableLine(captionTable,cDot,captionTableText[ngrp][1], captionTableText[ngrp][2], captionTableText[ngrp][3], captionTableText[ngrp][4])
end
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
-- local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<div class = "wikitable mw-collapsible ')
if string.find(autocaption,'collapsed') then table.insert(result,'mw-collapsed') end
table.insert(result,'" style="width:100%; line-height: 17px; border-style:none; text-align: center"><b>'..(args.toggletext or toggletext)..'</b>')
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if captionTable ~= '' then table.insert(result,captionTable..'</table>') end
if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
if maplist.width >= 400 and not(args['show-q-values']) then args['show-q-values'] = '1' -- default to on if wider than 400px
elseif maplist.width < 320 then args['show-q-values'] = '0' end -- turn off Qvalus if frame is too narrow, even if requested
return p._main(args)
end
return p
gqsmiqj591a951mg947o6gd8jfmzvze
204449
204448
2026-07-05T14:35:47Z
HaxelKaszëba
17492
204449
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
local geoTotalPop = 0
local captionTable = ''
local captionTableText = {}
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it may at some point be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
-- -------------- the following text variables are used within the template, and can be translated as required ---------------
-- -------------- you may wish to retain the English version as a comment, to keep track of the original phrase --------------
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext = 'Click for interactive fullscreen map with links to nearby articles'
local toggletext = 'List of numbered map items:'
local termsOfUse = 'Maps: terms of use'
local aboutOSM = 'About OpenStreetMaps'
local yards = 'yds'
local miles = 'miles'
local metres = 'm'
local km = 'km'
local sqkm = 'sq.km'
local year = 'year'
local notFound = 'not found'
local totalItems = 'Total'
local sumOfDatapoints = 'sum of datapoints'
local numberCol = 'No.' -- abbreviated for top of column of code numbers
local wikidataLink = 'Wikidata page'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.semicircle = "M0,5 C0,18.3,20,18.3,20,5 L 0,5z"
pathshape.arrowhead = "M 10,10.2 l 10,2.8 -2,-3 2,-3 -10,2.8 -0.2,0.2z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]') )
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
local t= string.find(label,'wikidata') -- exclude wikidata from tooltip line
if t then
label=string.sub(label,1,t-2) -- don't show wikidata value in tooltip
if string.find(link,'wikidata') then link = '' end -- don't link on click to wikidata (if there was not another link)
end
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r,test)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x,y
x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.075*(math.abs(t.latbase)/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat,magVal)
lat = tonumber(lat)
if not lat then
error("getScale: lat is nil")
end
if magVal and magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y=(round(dist*10,1))
return tostring(y*100)..metres, tostring(round(y*109,0))..yards
elseif dist <18 then
y=(round(dist,0))
return tostring(y)..km, tostring(round(y*0.621371,1))..miles
elseif dist <500 then
y=round(dist/10)
return tostring(y*10)..km, tostring(round(y*6.21371,0))..miles
else
y=round(dist/100)
return tostring(y*100)..km, tostring(round(y*62.1371,0))..miles
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function htmlTableLine (htmlTable,h1,h2,h3,h4,h5,type)
if type == 'header' then
if (h4 or '') ~= 'no-year' then h4='<th class="unsortable">'..h4 or ''..'</th>' else h4 = '' end
if (h5 or '') ~= '' then h5='<th class="unsortable">'..h5..'</th>' else h5 = '' end
return htmlTable..'<tr><th>'..(h1 or '')..'</th><th>'..(h2 or '')..'</th><th>'..(h3 or '')..'</th>'..h4..h5..'</tr>'
else
if (h4 or '') ~= 'no-year' then h4='<td>'..h4 or ''..'</td>' else h4 = '' end
if (h5 or '') ~= '' then h5='<td>'..h5..'</td>' else h5 = '' end
return htmlTable..'<tr><td>'..(h1 or '')..'</td><td>'..(h2 or '')..'</td><td>'..(h3 or '')..'</td>'..h4..h5..'</tr>'
end
end
local function formatInt(number)
if number == 0 then return notFound end
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
int = int:reverse():gsub("(%d%d%d)", "%1,")
return minus .. int:reverse():gsub("^,", "") .. fraction
end
local function convertToSqKm (value,punit)
if string.find(punit, 'Q35852') then value = value / 100 -- convert to sq km if it's hectares
elseif string.find(punit, 'Q232291') then value = value /(1/2.59) -- or this if it's square miles
elseif string.find(punit, 'Q81292') then value = value / 247.1 -- or this if it's acres
end
return value
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
local qvalue=''
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
-- Assign autovalues if a wikidata Qvalue has been supplied
-- some preliminary documentation
-- geo-data-type= default is P1082 (population)
-- other options might include number of households (P1538) and per capita income (P10622) or land area (P2046)
-- geo-data-list= this is a comma-separated list of Q values, for a manually generated list that doesn't require nymbered parameters
-- geo-scalefactor = can enlarge or shrink all the dots in proportion, depending an what the map needs
-- geo-region = needs to be a Qvalue of the larger administrative region that the settlements/sub-regions are in
-- geo-sub-region needs to be a Pvalue, but will default to P150 (some pages use 'has parts' = P527)
-- geo-data3= etc numbered Qvalue item, if used in conjunction with other shape/label parameters
-- shapeD= sets default shape, which migh be n-circle to include numbers.
-- geo-number-size= will give a fixed size for shape numbers. (defaults to 12 when using proportional dots)
-- geo-boundaries= if set to 1 will add any Qvalues for the sub-regions to 'map-data-light'.
-- show-geo-dots= to use proportional dots. Will be fixed size if 0. (set mark-sizeD=0 to have no dot at all, eg to show labels or boundaries)
-- find-geo-dots=1 or 0 (needs to also include geo-region)
-- label-posD = gets additional options: [off] gives no label, [on] will position lower centre,
-- [on+] includes numeric values beside/below label, [on^] will always put it below. [on-] always puts values beside
if args['geo-data'..argval] then -- and (args['mark-title'..argval] or '') ~= 'none' then
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
if not args['geo-number-size'] then args['geo-number-size']=12 end
if not args['label-posD'] then args['label-posD'] = 'on' end
qvalue=args['geo-data'..argval]
-- total poulation is a Qvalue wikidata page entry, or add up all the geo-data values or use a random 10000
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local thisPop=0
local areatxt=''
local yearVal=''
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
thisPop=tonumber(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.amount) or 0
if pvalue == 'P2046' then -- area, so convert from ha to sq km if needed (/100)
thisPop = convertToSqKm(thisPop, mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit)
end
if mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers and mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585 then
yearVal = mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal then yearVal = yearVal:sub(2,5) end -- wikidata reports an entire date and time, so extract the year
end
else
debugmsg(mw.addWarning(mw.wikibase.getLabel(qvalue)..' ('..qvalue..') has no recorded '..plabel))
end
local scalefactor=args['geo-scalefactor'] or 1
if argval=='1' and args['mark-size1'] and not args['mark-sizeD'] then args['mark-sizeD'] = args['mark-size1'] end
if argval=='1' and args['label-pos1'] and not args['label-posD'] then args['label-posD'] = args['label-pos1'] end
if not args['mark-size'..argval] then
if args['show-geo-dots'] == '0' then
args['mark-size'..argval] = args['mark-sizeD'] or '1' -- just use a 'point' for the label to attach to
else
if thisPop>0 then
args['mark-size'..argval] = round(math.sqrt(thisPop/geoTotalPop*100) * 8 * scalefactor,0)
else
args['mark-size'..argval] = args['mark-sizeD'] or '1'
args['shape-outline'..argval]=args['shape-outline'..argval] or args['shape-outlineH']
end
end
end
local posTemp=string.lower(multiCheck(args,'label-pos',argval,'on'))
args['label-pos'..argval] = posTemp
posTemp=splitItem(posTemp,2)[1]
if pvalue == 'P2046' then
thisPop = round(thisPop,2)
areatxt=sqkm..areatxt -- round decimals if sq km area
elseif pvalue == 'P10622' then
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
local punit=(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit or ''):match("Q.*") -- extract Q value
if punit == 'Q4917' then areatxt=' US$'
elseif punit == 'Q4916' then areatxt=' euro'
elseif punit == 'Q25224' then areatxt=' £GBP'
else areatxt=mw.wikibase.getLabel(punit)..areatxt
end
end
end
if posTemp=='off' then
else
if not args['label'..argval] then args['label'..argval] = mw.wikibase.getLabel(qvalue) end
local tline=posTemp:find("[%+%^%-]")
if tline then
local tval=posTemp:sub(tline,tline)
local eline
posTemp=posTemp:sub(1,tline-1)
if posTemp == 'left' or posTemp == 'right' or string.find(posTemp,'east') or string.find(posTemp,'west') then
if tval == '^' then eline = '^' else eline = ' ' end -- only add line break if 'forced', used for left/right
elseif tval == '+' or tval == '^' then eline = '^' -- add linebreak unless 'forced' not to
else eline = ' ' -- must be a dash, so no line break
end
args['label'..argval] = args['label'..argval]..eline..'('..formatInt(thisPop)..')'
args['label-pos'..argval] = posTemp..args['label-pos'..argval]:sub(tline+1,#args['label-pos'..argval])
end
end
if posTemp=='on' then
if argval=='1' and (not args.ldyD) then args.ldyD = args['ldy'..argval] or '0' end
if not args['ldy'..argval] then args['ldy'..argval] = args.ldyD end -- ensure ldy for that dot is added to the position
args['label-pos'..argval]='center'
local _,br = string.gsub(args['label'..argval],'%^','')
local item=splitItem(args['label-size'..argval],2)
args['ldy'..argval] = (args['ldy'..argval] or 0) + ((item[1] or 11) * ((br+1)*0.6)) + ((args['geo-number-size'] or 12) / 2)
end
local nt=''
if args['mark-title'..argval] == 'nolist' then nt='nolist,' end
if not args['mark-title'..argval] or args['mark-title'..argval] == '' or args['mark-title'..argval] == 'nolist' then
if mw.wikibase.getSitelink(qvalue) then
args['mark-title'..argval] = nt..'[['..mw.wikibase.getSitelink(qvalue)..']]'
else
args['mark-title'..argval] = nt..mw.wikibase.getLabel(qvalue)
end
end -- If mark-title is not set locally then attempt Sitelink value, or use label
if thisPop == 0 then areatxt='' end
captionTableText[argval]={}
captionTableText[argval][1]= args['mark-title'..argval]
captionTableText[argval][2]= formatInt(thisPop)
if maplist.width >= 280 then captionTableText[argval][3]= yearVal else captionTableText[argval][3]='no-year' end
if args['show-q-values'] == '1' then
captionTableText[argval][4]='<small>[[wikidata:'..qvalue..'|'..qvalue..']]</small>'
else
local nbsp = ' '
if maplist.width < 230 then nbsp = ' ' end
captionTableText[argval][4]=''
if captionTableText[argval][3] == 'no-year' then
captionTableText[argval][2] = captionTableText[argval][2]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
else
captionTableText[argval][3] = captionTableText[argval][3]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
end
end
if args['mark-title'..argval] == 'none' then
-- no title
else
args['mark-title'..argval] = args['mark-title'..argval]..' ('..plabel..' '..formatInt(thisPop)..areatxt..')'
end
if not args['mark-description'..argval] then args['mark-description'..argval] = (mw.wikibase.getDescription(qvalue) or '')..'<br>' end
if (not args['mark-image'..argval]) and (mw.wikibase.getBestStatements(qvalue,'P18')[1]) then
args['mark-image'..argval] = mw.wikibase.getBestStatements(qvalue,'P18')[1].mainsnak.datavalue.value
end
item = multiCheck(args,'shape',argval,'circle') --default to circle if geo-data
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
else
item = multiCheck(args,'shape',argval,'image')
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
end
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,(shapeResult[nval].shapeSize or '14px')) -- add geo-data as fallback if already assigned
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
if args['shape-angle'..argval] then shapeResult[nval].shapeAngleArg=true else shapeResult[nval].shapeAngleArg=false end
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
shapeResult[nval].textAT=args['label-attribute'..argval] or ''
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
if args['label-angle'..argval] then shapeResult[nval].textNGarg=true else shapeResult[nval].textNGarg=false end
--sgd=dotTag attributes
shapeResult[nval].tagSize=(args['geo-number-size'] or tostring(shapeHeight*1.5))..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
if itemTab[1] == nil or itemTab[2] == nil then
debugmsg(mw.addWarning('Unable to read coordinates for mark-coord'..argval))
else
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
end
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
if string.find(item,'nolist') then dotItem.param1='nolist,' item = string.sub(args['mark-title'..argval],8) or args['label'..argval] end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
-- debugmsg('dlink for '..item..' is '..dotItem.dlink)
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
-- debugmsg(dotItem.code..' = '..dotItem.lat..','..dotItem.lon..': '..dotItem.labelText..' - '..dotItem.title..', '..shapeResult[nval].shapeSize)
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['geo-data'..x] and (not args['mark-coord'..x]) and (not args['mark-lat'..x]) then
-- get lat and lon values from wikidata and put them into the args.lat and args.lon for use wherever needed
if (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1]) then
args['mark-lat'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.latitude) or '0'
args['mark-lon'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.longitude) or '0'
local p=args['geo-data-type'] or 'P1082'
if mw.wikibase.getBestStatements(args['geo-data'..x],p)[1] then
local n = tonumber(mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.amount) or 0
if p == 'P2046' then -- area, so convert to sq km if needed (/100)
n = convertToSqKm( n, mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.unit)
end
geoTotalPop = geoTotalPop + n
end
else debugmsg(mw.addWarning('could not find coords for '..mw.wikibase.getLabel(args['geo-data'..x])..', '..args['geo-data'..x]))
end
end
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form (needs a second table, seemingly)
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
local unitConvert=1
local areaUnits=''
if args['geo-region'] and (args['find-geo-dots'] or '1') ~= '0' then
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
local subhead='' -- Assemble the subhead content, a line of text about the region as a whole
if region and mw.wikibase.getBestStatements(region,pvalue)[1] then
local punit=mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.unit:match("Q.*") -- extract Q value
if pvalue == 'P10622' then -- add other currency-based pvalues here
areaUnits=mw.wikibase.getLabel(punit)
end
if punit == 'Q35852' then unitConvert=100 areaUnits = ' '..sqkm -- if its hectares, convert to sq km
elseif punit == 'Q232291' then unitConvert = (1/2.59) areaUnits = ' '..sqkm -- or this if it's square miles
elseif punit == 'Q81292' then unitConvert = 247.1 areaUnits = ' '..sqkm -- or this if it's acres
elseif punit == 'Q712226' then unitConvert = 1 areaUnits = ' '..sqkm -- or this if it's already sq km
elseif punit == 'Q4917' then areaUnits=' US$' -- replace long version with some shortform currencies ()
elseif punit == 'Q4916' then areaUnits=' euro'
elseif punit == 'Q25224' then areaUnits=' £GBP'
end
-- if pvalue == 'P2046' then
-- may need to add other instances of sq km so add in other P-items here if they need units showing
-- end
geoTotalPop=(mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.amount) / unitConvert
local yearVal
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers then
yearVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal and yearVal:sub(1,1) == '+' then yearVal = yearVal:sub(2,5)
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459 and mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1] then
local methodVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1].datavalue.value.id
if methodVal then yearVal=yearVal..' '..mw.wikibase.getLabel(methodVal) end
end
yearVal = ' ('..yearVal..')'
else yearVal =''
end
end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ([[wikidata:'..region..']]) = '..formatInt(round(geoTotalPop),1)..areaUnits..yearVal
else
if pvalue == 'P2046' then areaUnits = sqkm end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ('..sumOfDatapoints..') = '..formatInt(round(geoTotalPop),1)..areaUnits
end
if maplist.width < 350 and #plabel > 4 then plabel=string.sub((plabel or ' '),1,3)..'.' end
if maplist.width < 330 then numberCol = ' ' end
if maplist.width < 280 then year = 'no-year' end
captionTable = subhead
if (args['show-q-values'] or '') ~= '1' then
captionTable = captionTable..'<div style="float:right;"><small> (WD = '..wikidataLink..')</small></div><br>'
wikidataLink=''
end
captionTable = captionTable..'<table class="wikitable plainrowheaders sortable mw-collapsible mw-collapsed" style="width: 100%;"><caption>'..(args.toggletext or toggletext)..' </caption>'
captionTable = htmlTableLine(captionTable,numberCol,mw.wikibase.getLabel(region),plabel..' '..areaUnits, year, wikidataLink,'header')
end
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double,shape)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
local tw
if double <= 1 then
tw = tonumber(string.match(oWid,"[%.%d]+"))/2 -- shift centre by half the line width
else
tw = tonumber(string.match(oWid,"[%.%d]+")) + (double/2) - 0.5 -- shift centre by a line width + half the gap
end
-- debugmsg(oWid..'+'..double..'/2='..tw)
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2) ).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) - tw).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
local lineAngle=math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi
-- if x1 == x2 then lineAngle=90 end
table.insert(result,"transform: rotate("..lineAngle.."deg);\"></div>")
-- debugmsg('text angle started as '..shape.textNG..' and lineAngle = '..lineAngle)
if (lineAngle < -90) or (lineAngle >90) then lineAngle=lineAngle-180 end
if shape and not shape.textNGarg then shape.textNG=tostring(round(lineAngle,0))..'deg' end -- Put in line angle for a default text angle as well
-- if (y2 >= y1) and (x2 <= x1) then lineAngle=lineAngle-180 debugmsg('arrow angle corrected') end
if x2 < x1 then lineAngle=lineAngle-180 end
if shape and not shape.shapeAngleArg and shape.shape == 'arrowhead' then shape.shapeAngle=tostring(round(lineAngle,0))..'deg' end -- line angle for arrowhead
-- debugmsg('text angle is now '..shape.textNG..'with arrow at '..lineAngle)
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if string.find(shape.shape, 'svgpath,') and #shape.shape > 9 then
pathshape.svgpath=string.sub(shape.shape,9)
shape.shape='svgpath'
end
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if shape.shape == 'arrowhead' then -- enlarge arrowheads, as they only go to the middle of the notional shape-size
w=w*2.5
h=h*2.5
end
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result," scale("..tostring(w/16)..", "..tostring(h/16)..")")
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy+2 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,rndg=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = (w/2)+1,(h/2)+1
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
-- debugmsg('r-(w/2) > widthzone r-w/2='..tostring(r-(w/2))..', widthzone='..widthzone..', label='..(row.labelText or '')..', postype='..(row.posType or ''))
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
-- debugmsg('n-line for '..row.code..':'..tostring(row.gridx+1)..', '..tostring(row.gridy-1)..', '..tostring(row.gridx+row.dx+offsetx)..', '..tostring(row.gridy+row.dy+offsety)..', with width '..tostring(shape.textEW)..'px '..(shape.textES or 'no style')..' and color '..shape.textEC)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap,shape)
-- debugmsg('mark-line line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..(row.mlStyle or 'no style')..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
if color[2] then
propertyTable['stroke-opacity'] = tonumber(color[2] or '20')/100
end
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] and args['map-data-light'] ~= '' then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.4 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
comma=', '
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
local function getQset (qValue,pValue,argNumber,args)
local result={}
local sortType=string.lower(args['geo-list-sort'] or 'a')
local testValue='0'
if sortType == 'alpha' or sortType == 'a' or sortType == 'ah' or sortType == 'al' then
testValue = mw.wikibase.getLabel(qValue)
elseif sortType == 'value' or sortType == 'v' or sortType == 'vh' or sortType == 'vl'then
if qValue==nil or pValue == nil then debugmsg(mw.addWarning((qValue or 'nil')..' Qnumber for '..(pValue or 'nil')))
else
if mw.wikibase.getBestStatements(qValue,pValue)[1] then
testValue = mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.amount or '0'
if pValue == 'P2046' then
testValue = convertToSqKm(testValue, mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.unit)
end
end
end
elseif sortType == 'north' or sortType == 'n' or sortType == 'nh' or sortType == 'nl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.latitude or 0
end
elseif sortType == 'west' or sortType == 'w' or sortType == 'wh' or sortType == 'wl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.longitude or 0
end
end
result[1]=testValue
result[2]=argNumber
return result
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb floatleft">')
else table.insert(result,'<div class="thumb floatright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
--add any boundary Qvalues to map-data-light if requested
if args['geo-region'] and (args['geo-boundaries'] == '1') then
local qmaps=args['map-data-light'] or ''
if qmaps ~='' and string.sub(qmaps,#qmaps) ~= ',' then qmaps=qmaps..',' end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
if mw.wikibase.entityExists(qtemp) then
qmaps=qmaps..qtemp..',' -- see if there is a geocode boundary Qvalue to add
else debugmsg(mw.addWarning('unable to add geocode value for '..mw.wikibase.getLabel..','..qtemp))
end
end
ct=ct+1
end
end
if qmaps and qmaps ~= '' then -- add back the created/expanded list of boundary Qvalues, and remove trailing comma
args['map-data-light'] = string.sub(qmaps,1,#qmaps-1)
end
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
local qset={}
for indx,val in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') or string.match(indx,'geo%-data[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
if string.match(indx,'geo%-data[%d]+') then
local argnum=string.sub(indx,9,#indx) or ''
if args['mark-title'..argnum] == 'none' or (string.find(args['mark-title'..argnum] or 'x','nolist')== 1) then
-- debugmsg('sort values ignored '..argnum..', '..args['mark-title'..argnum])
else
qset[val]=getQset (val, (args['geo-data-type'] or 'P1082'), argnum, args)
end
end
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
local qlist=args['geo-data-list']
if qlist then
if string.sub(qlist,#qlist) ~= ',' then qlist=qlist..',' end
else
qlist=''
end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if (args['find-geo-dots'] or '1') ~= '0' and geoRegion and mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(adminRegion,adminSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
qlist=qlist..qtemp..',' -- add subregion Qvalue to the list
end
ct=ct+1
end
end
if qlist and #qlist>1 then -- create a table from the csv list
local _, c = qlist:gsub(",","")
local qtab=splitItem(qlist,c)
for xd,yd in pairs(qtab) do
yd=string.upper(yd)
if qset[yd] == nil and mw.wikibase.isValidEntityId(yd) then
dotmax=dotmax+1
qset[yd]=getQset (yd, (args['geo-data-type'] or 'P1082'), tostring(dotmax), args)
args['geo-data'..tostring(dotmax)] = yd
-- debugmsg(dotmax..' = '..(string.upper(yd) or 'Q Not found')..': '..(mw.wikibase.getLabel(yd) or 'label Not found')..': '..(mw.wikibase.getSitelink(yd) or 'link Not found'))
elseif not mw.wikibase.isValidEntityId(yd) then
debugmsg(mw.addWarning(yd..' is not a [[wikidata]] page. geo-data-list needs comma-separated Qvalues' ))
else debugmsg(mw.addWarning(yd..' is duplicate'))
end
end
end
-- qset contains set of Qvalues and can be from wikidata geo-region, list of geo-data-list Q values or individual geo-data1= etc
local ts = {} -- table to hold the keys
local useQset=false
for ky, val in pairs(qset) do
table.insert(ts, {key = ky, value = val[1], num=val[2]})
useQset=true
end
if useQset then
local order = string.lower(args['geo-list-sort'] or 'a') -- sort the list from previously stored sortkey, either Lowest or Highest first
if string.find(',al,vl,nl,wl,west,w,alpha,a,' , ','..order..',') then
table.sort(ts, function (a,b) return lessthan(a.value, b.value) end)
else
table.sort(ts, function (a,b) return morethan(a.value, b.value) end)
end
for ky,val in ipairs(ts) do -- assign values from sorted list to numbered parameter
args['numbered'..val.num] = ky -- The numbered parameter becomes the value for each dot that has a geo Qvalue, from manual, list or wiki sources.
end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) or args['geo-data'..x] then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,round(dotitem.lat,5),round(dotitem.lon,5),fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
if captionTable ~= '' and (not args['auto-caption']) then args['auto-caption']='on' end --default to include list if using geo values
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if negativeAnswer[autoOff] then captionTable='' end
if args.caption or (not negativeAnswer[autoOff]) then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then
table.insert(result,args.caption)
if captionTable ~= '' then table.insert(result,'<hr>') end
end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
if captionTable == '' then
capchk=true
else -- add the next line of content to the html table
capchk=false
end
end
end
if captionTable ~= '' then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
for k,v in pairs(captionList) do
local nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
local ngrp=v.value.group
if captionTableText[ngrp] then
-- debugmsg('table text for'..v.value.dotTag..', '..captionTableText[v.value.group][1]..', '..captionTableText[v.value.group][2]..', '..captionTableText[v.value.group][3]..' ('..captionTableText[v.value.group][4]..')')
local c1,c2
c1,c2=checkColors(shapeList[ngrp].shapeColor)
local cDot='<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;'
cDot=cDot..'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div>'
captionTable = htmlTableLine(captionTable,cDot,captionTableText[ngrp][1], captionTableText[ngrp][2], captionTableText[ngrp][3], captionTableText[ngrp][4])
end
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
-- local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<div class = "wikitable mw-collapsible ')
if string.find(autocaption,'collapsed') then table.insert(result,'mw-collapsed') end
table.insert(result,'" style="width:100%; line-height: 17px; border-style:none; text-align: center"><b>'..(args.toggletext or toggletext)..'</b>')
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if captionTable ~= '' then table.insert(result,captionTable..'</table>') end
if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
if maplist.width >= 400 and not(args['show-q-values']) then args['show-q-values'] = '1' -- default to on if wider than 400px
elseif maplist.width < 320 then args['show-q-values'] = '0' end -- turn off Qvalus if frame is too narrow, even if requested
return p._main(args)
end
return p
dtk9dp67voi5ll81vk4xxue11pgzo4j
204450
204449
2026-07-05T14:37:27Z
HaxelKaszëba
17492
204450
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
local geoTotalPop = 0
local captionTable = ''
local captionTableText = {}
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it may at some point be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
-- -------------- the following text variables are used within the template, and can be translated as required ---------------
-- -------------- you may wish to retain the English version as a comment, to keep track of the original phrase --------------
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext = 'Click for interactive fullscreen map with links to nearby articles'
local toggletext = 'List of numbered map items:'
local termsOfUse = 'Maps: terms of use'
local aboutOSM = 'About OpenStreetMaps'
local yards = 'yds'
local miles = 'miles'
local metres = 'm'
local km = 'km'
local sqkm = 'sq.km'
local year = 'year'
local notFound = 'not found'
local totalItems = 'Total'
local sumOfDatapoints = 'sum of datapoints'
local numberCol = 'No.' -- abbreviated for top of column of code numbers
local wikidataLink = 'Wikidata page'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.semicircle = "M0,5 C0,18.3,20,18.3,20,5 L 0,5z"
pathshape.arrowhead = "M 10,10.2 l 10,2.8 -2,-3 2,-3 -10,2.8 -0.2,0.2z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]') )
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
local t= string.find(label,'wikidata') -- exclude wikidata from tooltip line
if t then
label=string.sub(label,1,t-2) -- don't show wikidata value in tooltip
if string.find(link,'wikidata') then link = '' end -- don't link on click to wikidata (if there was not another link)
end
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r,test)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x,y
x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.075*(math.abs(t.latbase)/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat,magVal)
lat = tonumber(lat)
if not lat then
error(
"zoom=" .. tostring(zoom) ..
" lat=" .. tostring(lat) ..
" mag=" .. tostring(magVal)
)
end
if magVal and magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y=(round(dist*10,1))
return tostring(y*100)..metres, tostring(round(y*109,0))..yards
elseif dist <18 then
y=(round(dist,0))
return tostring(y)..km, tostring(round(y*0.621371,1))..miles
elseif dist <500 then
y=round(dist/10)
return tostring(y*10)..km, tostring(round(y*6.21371,0))..miles
else
y=round(dist/100)
return tostring(y*100)..km, tostring(round(y*62.1371,0))..miles
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function htmlTableLine (htmlTable,h1,h2,h3,h4,h5,type)
if type == 'header' then
if (h4 or '') ~= 'no-year' then h4='<th class="unsortable">'..h4 or ''..'</th>' else h4 = '' end
if (h5 or '') ~= '' then h5='<th class="unsortable">'..h5..'</th>' else h5 = '' end
return htmlTable..'<tr><th>'..(h1 or '')..'</th><th>'..(h2 or '')..'</th><th>'..(h3 or '')..'</th>'..h4..h5..'</tr>'
else
if (h4 or '') ~= 'no-year' then h4='<td>'..h4 or ''..'</td>' else h4 = '' end
if (h5 or '') ~= '' then h5='<td>'..h5..'</td>' else h5 = '' end
return htmlTable..'<tr><td>'..(h1 or '')..'</td><td>'..(h2 or '')..'</td><td>'..(h3 or '')..'</td>'..h4..h5..'</tr>'
end
end
local function formatInt(number)
if number == 0 then return notFound end
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
int = int:reverse():gsub("(%d%d%d)", "%1,")
return minus .. int:reverse():gsub("^,", "") .. fraction
end
local function convertToSqKm (value,punit)
if string.find(punit, 'Q35852') then value = value / 100 -- convert to sq km if it's hectares
elseif string.find(punit, 'Q232291') then value = value /(1/2.59) -- or this if it's square miles
elseif string.find(punit, 'Q81292') then value = value / 247.1 -- or this if it's acres
end
return value
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
local qvalue=''
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
-- Assign autovalues if a wikidata Qvalue has been supplied
-- some preliminary documentation
-- geo-data-type= default is P1082 (population)
-- other options might include number of households (P1538) and per capita income (P10622) or land area (P2046)
-- geo-data-list= this is a comma-separated list of Q values, for a manually generated list that doesn't require nymbered parameters
-- geo-scalefactor = can enlarge or shrink all the dots in proportion, depending an what the map needs
-- geo-region = needs to be a Qvalue of the larger administrative region that the settlements/sub-regions are in
-- geo-sub-region needs to be a Pvalue, but will default to P150 (some pages use 'has parts' = P527)
-- geo-data3= etc numbered Qvalue item, if used in conjunction with other shape/label parameters
-- shapeD= sets default shape, which migh be n-circle to include numbers.
-- geo-number-size= will give a fixed size for shape numbers. (defaults to 12 when using proportional dots)
-- geo-boundaries= if set to 1 will add any Qvalues for the sub-regions to 'map-data-light'.
-- show-geo-dots= to use proportional dots. Will be fixed size if 0. (set mark-sizeD=0 to have no dot at all, eg to show labels or boundaries)
-- find-geo-dots=1 or 0 (needs to also include geo-region)
-- label-posD = gets additional options: [off] gives no label, [on] will position lower centre,
-- [on+] includes numeric values beside/below label, [on^] will always put it below. [on-] always puts values beside
if args['geo-data'..argval] then -- and (args['mark-title'..argval] or '') ~= 'none' then
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
if not args['geo-number-size'] then args['geo-number-size']=12 end
if not args['label-posD'] then args['label-posD'] = 'on' end
qvalue=args['geo-data'..argval]
-- total poulation is a Qvalue wikidata page entry, or add up all the geo-data values or use a random 10000
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local thisPop=0
local areatxt=''
local yearVal=''
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
thisPop=tonumber(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.amount) or 0
if pvalue == 'P2046' then -- area, so convert from ha to sq km if needed (/100)
thisPop = convertToSqKm(thisPop, mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit)
end
if mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers and mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585 then
yearVal = mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal then yearVal = yearVal:sub(2,5) end -- wikidata reports an entire date and time, so extract the year
end
else
debugmsg(mw.addWarning(mw.wikibase.getLabel(qvalue)..' ('..qvalue..') has no recorded '..plabel))
end
local scalefactor=args['geo-scalefactor'] or 1
if argval=='1' and args['mark-size1'] and not args['mark-sizeD'] then args['mark-sizeD'] = args['mark-size1'] end
if argval=='1' and args['label-pos1'] and not args['label-posD'] then args['label-posD'] = args['label-pos1'] end
if not args['mark-size'..argval] then
if args['show-geo-dots'] == '0' then
args['mark-size'..argval] = args['mark-sizeD'] or '1' -- just use a 'point' for the label to attach to
else
if thisPop>0 then
args['mark-size'..argval] = round(math.sqrt(thisPop/geoTotalPop*100) * 8 * scalefactor,0)
else
args['mark-size'..argval] = args['mark-sizeD'] or '1'
args['shape-outline'..argval]=args['shape-outline'..argval] or args['shape-outlineH']
end
end
end
local posTemp=string.lower(multiCheck(args,'label-pos',argval,'on'))
args['label-pos'..argval] = posTemp
posTemp=splitItem(posTemp,2)[1]
if pvalue == 'P2046' then
thisPop = round(thisPop,2)
areatxt=sqkm..areatxt -- round decimals if sq km area
elseif pvalue == 'P10622' then
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
local punit=(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit or ''):match("Q.*") -- extract Q value
if punit == 'Q4917' then areatxt=' US$'
elseif punit == 'Q4916' then areatxt=' euro'
elseif punit == 'Q25224' then areatxt=' £GBP'
else areatxt=mw.wikibase.getLabel(punit)..areatxt
end
end
end
if posTemp=='off' then
else
if not args['label'..argval] then args['label'..argval] = mw.wikibase.getLabel(qvalue) end
local tline=posTemp:find("[%+%^%-]")
if tline then
local tval=posTemp:sub(tline,tline)
local eline
posTemp=posTemp:sub(1,tline-1)
if posTemp == 'left' or posTemp == 'right' or string.find(posTemp,'east') or string.find(posTemp,'west') then
if tval == '^' then eline = '^' else eline = ' ' end -- only add line break if 'forced', used for left/right
elseif tval == '+' or tval == '^' then eline = '^' -- add linebreak unless 'forced' not to
else eline = ' ' -- must be a dash, so no line break
end
args['label'..argval] = args['label'..argval]..eline..'('..formatInt(thisPop)..')'
args['label-pos'..argval] = posTemp..args['label-pos'..argval]:sub(tline+1,#args['label-pos'..argval])
end
end
if posTemp=='on' then
if argval=='1' and (not args.ldyD) then args.ldyD = args['ldy'..argval] or '0' end
if not args['ldy'..argval] then args['ldy'..argval] = args.ldyD end -- ensure ldy for that dot is added to the position
args['label-pos'..argval]='center'
local _,br = string.gsub(args['label'..argval],'%^','')
local item=splitItem(args['label-size'..argval],2)
args['ldy'..argval] = (args['ldy'..argval] or 0) + ((item[1] or 11) * ((br+1)*0.6)) + ((args['geo-number-size'] or 12) / 2)
end
local nt=''
if args['mark-title'..argval] == 'nolist' then nt='nolist,' end
if not args['mark-title'..argval] or args['mark-title'..argval] == '' or args['mark-title'..argval] == 'nolist' then
if mw.wikibase.getSitelink(qvalue) then
args['mark-title'..argval] = nt..'[['..mw.wikibase.getSitelink(qvalue)..']]'
else
args['mark-title'..argval] = nt..mw.wikibase.getLabel(qvalue)
end
end -- If mark-title is not set locally then attempt Sitelink value, or use label
if thisPop == 0 then areatxt='' end
captionTableText[argval]={}
captionTableText[argval][1]= args['mark-title'..argval]
captionTableText[argval][2]= formatInt(thisPop)
if maplist.width >= 280 then captionTableText[argval][3]= yearVal else captionTableText[argval][3]='no-year' end
if args['show-q-values'] == '1' then
captionTableText[argval][4]='<small>[[wikidata:'..qvalue..'|'..qvalue..']]</small>'
else
local nbsp = ' '
if maplist.width < 230 then nbsp = ' ' end
captionTableText[argval][4]=''
if captionTableText[argval][3] == 'no-year' then
captionTableText[argval][2] = captionTableText[argval][2]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
else
captionTableText[argval][3] = captionTableText[argval][3]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
end
end
if args['mark-title'..argval] == 'none' then
-- no title
else
args['mark-title'..argval] = args['mark-title'..argval]..' ('..plabel..' '..formatInt(thisPop)..areatxt..')'
end
if not args['mark-description'..argval] then args['mark-description'..argval] = (mw.wikibase.getDescription(qvalue) or '')..'<br>' end
if (not args['mark-image'..argval]) and (mw.wikibase.getBestStatements(qvalue,'P18')[1]) then
args['mark-image'..argval] = mw.wikibase.getBestStatements(qvalue,'P18')[1].mainsnak.datavalue.value
end
item = multiCheck(args,'shape',argval,'circle') --default to circle if geo-data
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
else
item = multiCheck(args,'shape',argval,'image')
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
end
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,(shapeResult[nval].shapeSize or '14px')) -- add geo-data as fallback if already assigned
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
if args['shape-angle'..argval] then shapeResult[nval].shapeAngleArg=true else shapeResult[nval].shapeAngleArg=false end
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
shapeResult[nval].textAT=args['label-attribute'..argval] or ''
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
if args['label-angle'..argval] then shapeResult[nval].textNGarg=true else shapeResult[nval].textNGarg=false end
--sgd=dotTag attributes
shapeResult[nval].tagSize=(args['geo-number-size'] or tostring(shapeHeight*1.5))..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
if itemTab[1] == nil or itemTab[2] == nil then
debugmsg(mw.addWarning('Unable to read coordinates for mark-coord'..argval))
else
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
end
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
if string.find(item,'nolist') then dotItem.param1='nolist,' item = string.sub(args['mark-title'..argval],8) or args['label'..argval] end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
-- debugmsg('dlink for '..item..' is '..dotItem.dlink)
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
-- debugmsg(dotItem.code..' = '..dotItem.lat..','..dotItem.lon..': '..dotItem.labelText..' - '..dotItem.title..', '..shapeResult[nval].shapeSize)
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['geo-data'..x] and (not args['mark-coord'..x]) and (not args['mark-lat'..x]) then
-- get lat and lon values from wikidata and put them into the args.lat and args.lon for use wherever needed
if (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1]) then
args['mark-lat'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.latitude) or '0'
args['mark-lon'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.longitude) or '0'
local p=args['geo-data-type'] or 'P1082'
if mw.wikibase.getBestStatements(args['geo-data'..x],p)[1] then
local n = tonumber(mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.amount) or 0
if p == 'P2046' then -- area, so convert to sq km if needed (/100)
n = convertToSqKm( n, mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.unit)
end
geoTotalPop = geoTotalPop + n
end
else debugmsg(mw.addWarning('could not find coords for '..mw.wikibase.getLabel(args['geo-data'..x])..', '..args['geo-data'..x]))
end
end
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form (needs a second table, seemingly)
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
local unitConvert=1
local areaUnits=''
if args['geo-region'] and (args['find-geo-dots'] or '1') ~= '0' then
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
local subhead='' -- Assemble the subhead content, a line of text about the region as a whole
if region and mw.wikibase.getBestStatements(region,pvalue)[1] then
local punit=mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.unit:match("Q.*") -- extract Q value
if pvalue == 'P10622' then -- add other currency-based pvalues here
areaUnits=mw.wikibase.getLabel(punit)
end
if punit == 'Q35852' then unitConvert=100 areaUnits = ' '..sqkm -- if its hectares, convert to sq km
elseif punit == 'Q232291' then unitConvert = (1/2.59) areaUnits = ' '..sqkm -- or this if it's square miles
elseif punit == 'Q81292' then unitConvert = 247.1 areaUnits = ' '..sqkm -- or this if it's acres
elseif punit == 'Q712226' then unitConvert = 1 areaUnits = ' '..sqkm -- or this if it's already sq km
elseif punit == 'Q4917' then areaUnits=' US$' -- replace long version with some shortform currencies ()
elseif punit == 'Q4916' then areaUnits=' euro'
elseif punit == 'Q25224' then areaUnits=' £GBP'
end
-- if pvalue == 'P2046' then
-- may need to add other instances of sq km so add in other P-items here if they need units showing
-- end
geoTotalPop=(mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.amount) / unitConvert
local yearVal
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers then
yearVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal and yearVal:sub(1,1) == '+' then yearVal = yearVal:sub(2,5)
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459 and mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1] then
local methodVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1].datavalue.value.id
if methodVal then yearVal=yearVal..' '..mw.wikibase.getLabel(methodVal) end
end
yearVal = ' ('..yearVal..')'
else yearVal =''
end
end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ([[wikidata:'..region..']]) = '..formatInt(round(geoTotalPop),1)..areaUnits..yearVal
else
if pvalue == 'P2046' then areaUnits = sqkm end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ('..sumOfDatapoints..') = '..formatInt(round(geoTotalPop),1)..areaUnits
end
if maplist.width < 350 and #plabel > 4 then plabel=string.sub((plabel or ' '),1,3)..'.' end
if maplist.width < 330 then numberCol = ' ' end
if maplist.width < 280 then year = 'no-year' end
captionTable = subhead
if (args['show-q-values'] or '') ~= '1' then
captionTable = captionTable..'<div style="float:right;"><small> (WD = '..wikidataLink..')</small></div><br>'
wikidataLink=''
end
captionTable = captionTable..'<table class="wikitable plainrowheaders sortable mw-collapsible mw-collapsed" style="width: 100%;"><caption>'..(args.toggletext or toggletext)..' </caption>'
captionTable = htmlTableLine(captionTable,numberCol,mw.wikibase.getLabel(region),plabel..' '..areaUnits, year, wikidataLink,'header')
end
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double,shape)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
local tw
if double <= 1 then
tw = tonumber(string.match(oWid,"[%.%d]+"))/2 -- shift centre by half the line width
else
tw = tonumber(string.match(oWid,"[%.%d]+")) + (double/2) - 0.5 -- shift centre by a line width + half the gap
end
-- debugmsg(oWid..'+'..double..'/2='..tw)
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2) ).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) - tw).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
local lineAngle=math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi
-- if x1 == x2 then lineAngle=90 end
table.insert(result,"transform: rotate("..lineAngle.."deg);\"></div>")
-- debugmsg('text angle started as '..shape.textNG..' and lineAngle = '..lineAngle)
if (lineAngle < -90) or (lineAngle >90) then lineAngle=lineAngle-180 end
if shape and not shape.textNGarg then shape.textNG=tostring(round(lineAngle,0))..'deg' end -- Put in line angle for a default text angle as well
-- if (y2 >= y1) and (x2 <= x1) then lineAngle=lineAngle-180 debugmsg('arrow angle corrected') end
if x2 < x1 then lineAngle=lineAngle-180 end
if shape and not shape.shapeAngleArg and shape.shape == 'arrowhead' then shape.shapeAngle=tostring(round(lineAngle,0))..'deg' end -- line angle for arrowhead
-- debugmsg('text angle is now '..shape.textNG..'with arrow at '..lineAngle)
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if string.find(shape.shape, 'svgpath,') and #shape.shape > 9 then
pathshape.svgpath=string.sub(shape.shape,9)
shape.shape='svgpath'
end
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if shape.shape == 'arrowhead' then -- enlarge arrowheads, as they only go to the middle of the notional shape-size
w=w*2.5
h=h*2.5
end
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result," scale("..tostring(w/16)..", "..tostring(h/16)..")")
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy+2 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,rndg=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = (w/2)+1,(h/2)+1
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
-- debugmsg('r-(w/2) > widthzone r-w/2='..tostring(r-(w/2))..', widthzone='..widthzone..', label='..(row.labelText or '')..', postype='..(row.posType or ''))
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
-- debugmsg('n-line for '..row.code..':'..tostring(row.gridx+1)..', '..tostring(row.gridy-1)..', '..tostring(row.gridx+row.dx+offsetx)..', '..tostring(row.gridy+row.dy+offsety)..', with width '..tostring(shape.textEW)..'px '..(shape.textES or 'no style')..' and color '..shape.textEC)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap,shape)
-- debugmsg('mark-line line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..(row.mlStyle or 'no style')..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
if color[2] then
propertyTable['stroke-opacity'] = tonumber(color[2] or '20')/100
end
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] and args['map-data-light'] ~= '' then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.4 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
comma=', '
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
local function getQset (qValue,pValue,argNumber,args)
local result={}
local sortType=string.lower(args['geo-list-sort'] or 'a')
local testValue='0'
if sortType == 'alpha' or sortType == 'a' or sortType == 'ah' or sortType == 'al' then
testValue = mw.wikibase.getLabel(qValue)
elseif sortType == 'value' or sortType == 'v' or sortType == 'vh' or sortType == 'vl'then
if qValue==nil or pValue == nil then debugmsg(mw.addWarning((qValue or 'nil')..' Qnumber for '..(pValue or 'nil')))
else
if mw.wikibase.getBestStatements(qValue,pValue)[1] then
testValue = mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.amount or '0'
if pValue == 'P2046' then
testValue = convertToSqKm(testValue, mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.unit)
end
end
end
elseif sortType == 'north' or sortType == 'n' or sortType == 'nh' or sortType == 'nl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.latitude or 0
end
elseif sortType == 'west' or sortType == 'w' or sortType == 'wh' or sortType == 'wl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.longitude or 0
end
end
result[1]=testValue
result[2]=argNumber
return result
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb floatleft">')
else table.insert(result,'<div class="thumb floatright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
--add any boundary Qvalues to map-data-light if requested
if args['geo-region'] and (args['geo-boundaries'] == '1') then
local qmaps=args['map-data-light'] or ''
if qmaps ~='' and string.sub(qmaps,#qmaps) ~= ',' then qmaps=qmaps..',' end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
if mw.wikibase.entityExists(qtemp) then
qmaps=qmaps..qtemp..',' -- see if there is a geocode boundary Qvalue to add
else debugmsg(mw.addWarning('unable to add geocode value for '..mw.wikibase.getLabel..','..qtemp))
end
end
ct=ct+1
end
end
if qmaps and qmaps ~= '' then -- add back the created/expanded list of boundary Qvalues, and remove trailing comma
args['map-data-light'] = string.sub(qmaps,1,#qmaps-1)
end
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
local qset={}
for indx,val in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') or string.match(indx,'geo%-data[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
if string.match(indx,'geo%-data[%d]+') then
local argnum=string.sub(indx,9,#indx) or ''
if args['mark-title'..argnum] == 'none' or (string.find(args['mark-title'..argnum] or 'x','nolist')== 1) then
-- debugmsg('sort values ignored '..argnum..', '..args['mark-title'..argnum])
else
qset[val]=getQset (val, (args['geo-data-type'] or 'P1082'), argnum, args)
end
end
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
local qlist=args['geo-data-list']
if qlist then
if string.sub(qlist,#qlist) ~= ',' then qlist=qlist..',' end
else
qlist=''
end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if (args['find-geo-dots'] or '1') ~= '0' and geoRegion and mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(adminRegion,adminSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
qlist=qlist..qtemp..',' -- add subregion Qvalue to the list
end
ct=ct+1
end
end
if qlist and #qlist>1 then -- create a table from the csv list
local _, c = qlist:gsub(",","")
local qtab=splitItem(qlist,c)
for xd,yd in pairs(qtab) do
yd=string.upper(yd)
if qset[yd] == nil and mw.wikibase.isValidEntityId(yd) then
dotmax=dotmax+1
qset[yd]=getQset (yd, (args['geo-data-type'] or 'P1082'), tostring(dotmax), args)
args['geo-data'..tostring(dotmax)] = yd
-- debugmsg(dotmax..' = '..(string.upper(yd) or 'Q Not found')..': '..(mw.wikibase.getLabel(yd) or 'label Not found')..': '..(mw.wikibase.getSitelink(yd) or 'link Not found'))
elseif not mw.wikibase.isValidEntityId(yd) then
debugmsg(mw.addWarning(yd..' is not a [[wikidata]] page. geo-data-list needs comma-separated Qvalues' ))
else debugmsg(mw.addWarning(yd..' is duplicate'))
end
end
end
-- qset contains set of Qvalues and can be from wikidata geo-region, list of geo-data-list Q values or individual geo-data1= etc
local ts = {} -- table to hold the keys
local useQset=false
for ky, val in pairs(qset) do
table.insert(ts, {key = ky, value = val[1], num=val[2]})
useQset=true
end
if useQset then
local order = string.lower(args['geo-list-sort'] or 'a') -- sort the list from previously stored sortkey, either Lowest or Highest first
if string.find(',al,vl,nl,wl,west,w,alpha,a,' , ','..order..',') then
table.sort(ts, function (a,b) return lessthan(a.value, b.value) end)
else
table.sort(ts, function (a,b) return morethan(a.value, b.value) end)
end
for ky,val in ipairs(ts) do -- assign values from sorted list to numbered parameter
args['numbered'..val.num] = ky -- The numbered parameter becomes the value for each dot that has a geo Qvalue, from manual, list or wiki sources.
end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) or args['geo-data'..x] then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,round(dotitem.lat,5),round(dotitem.lon,5),fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
if captionTable ~= '' and (not args['auto-caption']) then args['auto-caption']='on' end --default to include list if using geo values
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if negativeAnswer[autoOff] then captionTable='' end
if args.caption or (not negativeAnswer[autoOff]) then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then
table.insert(result,args.caption)
if captionTable ~= '' then table.insert(result,'<hr>') end
end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
if captionTable == '' then
capchk=true
else -- add the next line of content to the html table
capchk=false
end
end
end
if captionTable ~= '' then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
for k,v in pairs(captionList) do
local nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
local ngrp=v.value.group
if captionTableText[ngrp] then
-- debugmsg('table text for'..v.value.dotTag..', '..captionTableText[v.value.group][1]..', '..captionTableText[v.value.group][2]..', '..captionTableText[v.value.group][3]..' ('..captionTableText[v.value.group][4]..')')
local c1,c2
c1,c2=checkColors(shapeList[ngrp].shapeColor)
local cDot='<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;'
cDot=cDot..'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div>'
captionTable = htmlTableLine(captionTable,cDot,captionTableText[ngrp][1], captionTableText[ngrp][2], captionTableText[ngrp][3], captionTableText[ngrp][4])
end
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
-- local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<div class = "wikitable mw-collapsible ')
if string.find(autocaption,'collapsed') then table.insert(result,'mw-collapsed') end
table.insert(result,'" style="width:100%; line-height: 17px; border-style:none; text-align: center"><b>'..(args.toggletext or toggletext)..'</b>')
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if captionTable ~= '' then table.insert(result,captionTable..'</table>') end
if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
if maplist.width >= 400 and not(args['show-q-values']) then args['show-q-values'] = '1' -- default to on if wider than 400px
elseif maplist.width < 320 then args['show-q-values'] = '0' end -- turn off Qvalus if frame is too narrow, even if requested
return p._main(args)
end
return p
ie9mb61diifnhnf838u73j21yrzoo0m
204451
204450
2026-07-05T14:40:26Z
HaxelKaszëba
17492
204451
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
local geoTotalPop = 0
local captionTable = ''
local captionTableText = {}
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it may at some point be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
-- -------------- the following text variables are used within the template, and can be translated as required ---------------
-- -------------- you may wish to retain the English version as a comment, to keep track of the original phrase --------------
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext = 'Click for interactive fullscreen map with links to nearby articles'
local toggletext = 'List of numbered map items:'
local termsOfUse = 'Maps: terms of use'
local aboutOSM = 'About OpenStreetMaps'
local yards = 'yds'
local miles = 'miles'
local metres = 'm'
local km = 'km'
local sqkm = 'sq.km'
local year = 'year'
local notFound = 'not found'
local totalItems = 'Total'
local sumOfDatapoints = 'sum of datapoints'
local numberCol = 'No.' -- abbreviated for top of column of code numbers
local wikidataLink = 'Wikidata page'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.semicircle = "M0,5 C0,18.3,20,18.3,20,5 L 0,5z"
pathshape.arrowhead = "M 10,10.2 l 10,2.8 -2,-3 2,-3 -10,2.8 -0.2,0.2z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]') )
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
local t= string.find(label,'wikidata') -- exclude wikidata from tooltip line
if t then
label=string.sub(label,1,t-2) -- don't show wikidata value in tooltip
if string.find(link,'wikidata') then link = '' end -- don't link on click to wikidata (if there was not another link)
end
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r,test)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x,y
x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.075*(math.abs(t.latbase)/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat, magVal)
zoom = tonumber(zoom) or 0
lat = tonumber(lat)
if not lat then
lat = 0
end
if magVal then
magVal = tonumber(magVal)
if magVal and magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y = round(dist * 10, 1)
return tostring(y * 100) .. metres,
tostring(round(y * 109, 0)) .. yards
elseif dist < 18 then
y = round(dist, 0)
return tostring(y) .. km,
tostring(round(y * 0.621371, 1)) .. miles
elseif dist < 500 then
y = round(dist / 10)
return tostring(y * 10) .. km,
tostring(round(y * 6.21371, 0)) .. miles
else
y = round(dist / 100)
return tostring(y * 100) .. km,
tostring(round(y * 62.1371, 0)) .. miles
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function htmlTableLine (htmlTable,h1,h2,h3,h4,h5,type)
if type == 'header' then
if (h4 or '') ~= 'no-year' then h4='<th class="unsortable">'..h4 or ''..'</th>' else h4 = '' end
if (h5 or '') ~= '' then h5='<th class="unsortable">'..h5..'</th>' else h5 = '' end
return htmlTable..'<tr><th>'..(h1 or '')..'</th><th>'..(h2 or '')..'</th><th>'..(h3 or '')..'</th>'..h4..h5..'</tr>'
else
if (h4 or '') ~= 'no-year' then h4='<td>'..h4 or ''..'</td>' else h4 = '' end
if (h5 or '') ~= '' then h5='<td>'..h5..'</td>' else h5 = '' end
return htmlTable..'<tr><td>'..(h1 or '')..'</td><td>'..(h2 or '')..'</td><td>'..(h3 or '')..'</td>'..h4..h5..'</tr>'
end
end
local function formatInt(number)
if number == 0 then return notFound end
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
int = int:reverse():gsub("(%d%d%d)", "%1,")
return minus .. int:reverse():gsub("^,", "") .. fraction
end
local function convertToSqKm (value,punit)
if string.find(punit, 'Q35852') then value = value / 100 -- convert to sq km if it's hectares
elseif string.find(punit, 'Q232291') then value = value /(1/2.59) -- or this if it's square miles
elseif string.find(punit, 'Q81292') then value = value / 247.1 -- or this if it's acres
end
return value
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
local qvalue=''
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
-- Assign autovalues if a wikidata Qvalue has been supplied
-- some preliminary documentation
-- geo-data-type= default is P1082 (population)
-- other options might include number of households (P1538) and per capita income (P10622) or land area (P2046)
-- geo-data-list= this is a comma-separated list of Q values, for a manually generated list that doesn't require nymbered parameters
-- geo-scalefactor = can enlarge or shrink all the dots in proportion, depending an what the map needs
-- geo-region = needs to be a Qvalue of the larger administrative region that the settlements/sub-regions are in
-- geo-sub-region needs to be a Pvalue, but will default to P150 (some pages use 'has parts' = P527)
-- geo-data3= etc numbered Qvalue item, if used in conjunction with other shape/label parameters
-- shapeD= sets default shape, which migh be n-circle to include numbers.
-- geo-number-size= will give a fixed size for shape numbers. (defaults to 12 when using proportional dots)
-- geo-boundaries= if set to 1 will add any Qvalues for the sub-regions to 'map-data-light'.
-- show-geo-dots= to use proportional dots. Will be fixed size if 0. (set mark-sizeD=0 to have no dot at all, eg to show labels or boundaries)
-- find-geo-dots=1 or 0 (needs to also include geo-region)
-- label-posD = gets additional options: [off] gives no label, [on] will position lower centre,
-- [on+] includes numeric values beside/below label, [on^] will always put it below. [on-] always puts values beside
if args['geo-data'..argval] then -- and (args['mark-title'..argval] or '') ~= 'none' then
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
if not args['geo-number-size'] then args['geo-number-size']=12 end
if not args['label-posD'] then args['label-posD'] = 'on' end
qvalue=args['geo-data'..argval]
-- total poulation is a Qvalue wikidata page entry, or add up all the geo-data values or use a random 10000
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local thisPop=0
local areatxt=''
local yearVal=''
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
thisPop=tonumber(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.amount) or 0
if pvalue == 'P2046' then -- area, so convert from ha to sq km if needed (/100)
thisPop = convertToSqKm(thisPop, mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit)
end
if mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers and mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585 then
yearVal = mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal then yearVal = yearVal:sub(2,5) end -- wikidata reports an entire date and time, so extract the year
end
else
debugmsg(mw.addWarning(mw.wikibase.getLabel(qvalue)..' ('..qvalue..') has no recorded '..plabel))
end
local scalefactor=args['geo-scalefactor'] or 1
if argval=='1' and args['mark-size1'] and not args['mark-sizeD'] then args['mark-sizeD'] = args['mark-size1'] end
if argval=='1' and args['label-pos1'] and not args['label-posD'] then args['label-posD'] = args['label-pos1'] end
if not args['mark-size'..argval] then
if args['show-geo-dots'] == '0' then
args['mark-size'..argval] = args['mark-sizeD'] or '1' -- just use a 'point' for the label to attach to
else
if thisPop>0 then
args['mark-size'..argval] = round(math.sqrt(thisPop/geoTotalPop*100) * 8 * scalefactor,0)
else
args['mark-size'..argval] = args['mark-sizeD'] or '1'
args['shape-outline'..argval]=args['shape-outline'..argval] or args['shape-outlineH']
end
end
end
local posTemp=string.lower(multiCheck(args,'label-pos',argval,'on'))
args['label-pos'..argval] = posTemp
posTemp=splitItem(posTemp,2)[1]
if pvalue == 'P2046' then
thisPop = round(thisPop,2)
areatxt=sqkm..areatxt -- round decimals if sq km area
elseif pvalue == 'P10622' then
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
local punit=(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit or ''):match("Q.*") -- extract Q value
if punit == 'Q4917' then areatxt=' US$'
elseif punit == 'Q4916' then areatxt=' euro'
elseif punit == 'Q25224' then areatxt=' £GBP'
else areatxt=mw.wikibase.getLabel(punit)..areatxt
end
end
end
if posTemp=='off' then
else
if not args['label'..argval] then args['label'..argval] = mw.wikibase.getLabel(qvalue) end
local tline=posTemp:find("[%+%^%-]")
if tline then
local tval=posTemp:sub(tline,tline)
local eline
posTemp=posTemp:sub(1,tline-1)
if posTemp == 'left' or posTemp == 'right' or string.find(posTemp,'east') or string.find(posTemp,'west') then
if tval == '^' then eline = '^' else eline = ' ' end -- only add line break if 'forced', used for left/right
elseif tval == '+' or tval == '^' then eline = '^' -- add linebreak unless 'forced' not to
else eline = ' ' -- must be a dash, so no line break
end
args['label'..argval] = args['label'..argval]..eline..'('..formatInt(thisPop)..')'
args['label-pos'..argval] = posTemp..args['label-pos'..argval]:sub(tline+1,#args['label-pos'..argval])
end
end
if posTemp=='on' then
if argval=='1' and (not args.ldyD) then args.ldyD = args['ldy'..argval] or '0' end
if not args['ldy'..argval] then args['ldy'..argval] = args.ldyD end -- ensure ldy for that dot is added to the position
args['label-pos'..argval]='center'
local _,br = string.gsub(args['label'..argval],'%^','')
local item=splitItem(args['label-size'..argval],2)
args['ldy'..argval] = (args['ldy'..argval] or 0) + ((item[1] or 11) * ((br+1)*0.6)) + ((args['geo-number-size'] or 12) / 2)
end
local nt=''
if args['mark-title'..argval] == 'nolist' then nt='nolist,' end
if not args['mark-title'..argval] or args['mark-title'..argval] == '' or args['mark-title'..argval] == 'nolist' then
if mw.wikibase.getSitelink(qvalue) then
args['mark-title'..argval] = nt..'[['..mw.wikibase.getSitelink(qvalue)..']]'
else
args['mark-title'..argval] = nt..mw.wikibase.getLabel(qvalue)
end
end -- If mark-title is not set locally then attempt Sitelink value, or use label
if thisPop == 0 then areatxt='' end
captionTableText[argval]={}
captionTableText[argval][1]= args['mark-title'..argval]
captionTableText[argval][2]= formatInt(thisPop)
if maplist.width >= 280 then captionTableText[argval][3]= yearVal else captionTableText[argval][3]='no-year' end
if args['show-q-values'] == '1' then
captionTableText[argval][4]='<small>[[wikidata:'..qvalue..'|'..qvalue..']]</small>'
else
local nbsp = ' '
if maplist.width < 230 then nbsp = ' ' end
captionTableText[argval][4]=''
if captionTableText[argval][3] == 'no-year' then
captionTableText[argval][2] = captionTableText[argval][2]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
else
captionTableText[argval][3] = captionTableText[argval][3]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
end
end
if args['mark-title'..argval] == 'none' then
-- no title
else
args['mark-title'..argval] = args['mark-title'..argval]..' ('..plabel..' '..formatInt(thisPop)..areatxt..')'
end
if not args['mark-description'..argval] then args['mark-description'..argval] = (mw.wikibase.getDescription(qvalue) or '')..'<br>' end
if (not args['mark-image'..argval]) and (mw.wikibase.getBestStatements(qvalue,'P18')[1]) then
args['mark-image'..argval] = mw.wikibase.getBestStatements(qvalue,'P18')[1].mainsnak.datavalue.value
end
item = multiCheck(args,'shape',argval,'circle') --default to circle if geo-data
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
else
item = multiCheck(args,'shape',argval,'image')
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
end
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,(shapeResult[nval].shapeSize or '14px')) -- add geo-data as fallback if already assigned
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
if args['shape-angle'..argval] then shapeResult[nval].shapeAngleArg=true else shapeResult[nval].shapeAngleArg=false end
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
shapeResult[nval].textAT=args['label-attribute'..argval] or ''
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
if args['label-angle'..argval] then shapeResult[nval].textNGarg=true else shapeResult[nval].textNGarg=false end
--sgd=dotTag attributes
shapeResult[nval].tagSize=(args['geo-number-size'] or tostring(shapeHeight*1.5))..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
if itemTab[1] == nil or itemTab[2] == nil then
debugmsg(mw.addWarning('Unable to read coordinates for mark-coord'..argval))
else
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
end
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
if string.find(item,'nolist') then dotItem.param1='nolist,' item = string.sub(args['mark-title'..argval],8) or args['label'..argval] end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
-- debugmsg('dlink for '..item..' is '..dotItem.dlink)
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
-- debugmsg(dotItem.code..' = '..dotItem.lat..','..dotItem.lon..': '..dotItem.labelText..' - '..dotItem.title..', '..shapeResult[nval].shapeSize)
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['geo-data'..x] and (not args['mark-coord'..x]) and (not args['mark-lat'..x]) then
-- get lat and lon values from wikidata and put them into the args.lat and args.lon for use wherever needed
if (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1]) then
args['mark-lat'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.latitude) or '0'
args['mark-lon'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.longitude) or '0'
local p=args['geo-data-type'] or 'P1082'
if mw.wikibase.getBestStatements(args['geo-data'..x],p)[1] then
local n = tonumber(mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.amount) or 0
if p == 'P2046' then -- area, so convert to sq km if needed (/100)
n = convertToSqKm( n, mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.unit)
end
geoTotalPop = geoTotalPop + n
end
else debugmsg(mw.addWarning('could not find coords for '..mw.wikibase.getLabel(args['geo-data'..x])..', '..args['geo-data'..x]))
end
end
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form (needs a second table, seemingly)
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
local unitConvert=1
local areaUnits=''
if args['geo-region'] and (args['find-geo-dots'] or '1') ~= '0' then
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
local subhead='' -- Assemble the subhead content, a line of text about the region as a whole
if region and mw.wikibase.getBestStatements(region,pvalue)[1] then
local punit=mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.unit:match("Q.*") -- extract Q value
if pvalue == 'P10622' then -- add other currency-based pvalues here
areaUnits=mw.wikibase.getLabel(punit)
end
if punit == 'Q35852' then unitConvert=100 areaUnits = ' '..sqkm -- if its hectares, convert to sq km
elseif punit == 'Q232291' then unitConvert = (1/2.59) areaUnits = ' '..sqkm -- or this if it's square miles
elseif punit == 'Q81292' then unitConvert = 247.1 areaUnits = ' '..sqkm -- or this if it's acres
elseif punit == 'Q712226' then unitConvert = 1 areaUnits = ' '..sqkm -- or this if it's already sq km
elseif punit == 'Q4917' then areaUnits=' US$' -- replace long version with some shortform currencies ()
elseif punit == 'Q4916' then areaUnits=' euro'
elseif punit == 'Q25224' then areaUnits=' £GBP'
end
-- if pvalue == 'P2046' then
-- may need to add other instances of sq km so add in other P-items here if they need units showing
-- end
geoTotalPop=(mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.amount) / unitConvert
local yearVal
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers then
yearVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal and yearVal:sub(1,1) == '+' then yearVal = yearVal:sub(2,5)
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459 and mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1] then
local methodVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1].datavalue.value.id
if methodVal then yearVal=yearVal..' '..mw.wikibase.getLabel(methodVal) end
end
yearVal = ' ('..yearVal..')'
else yearVal =''
end
end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ([[wikidata:'..region..']]) = '..formatInt(round(geoTotalPop),1)..areaUnits..yearVal
else
if pvalue == 'P2046' then areaUnits = sqkm end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ('..sumOfDatapoints..') = '..formatInt(round(geoTotalPop),1)..areaUnits
end
if maplist.width < 350 and #plabel > 4 then plabel=string.sub((plabel or ' '),1,3)..'.' end
if maplist.width < 330 then numberCol = ' ' end
if maplist.width < 280 then year = 'no-year' end
captionTable = subhead
if (args['show-q-values'] or '') ~= '1' then
captionTable = captionTable..'<div style="float:right;"><small> (WD = '..wikidataLink..')</small></div><br>'
wikidataLink=''
end
captionTable = captionTable..'<table class="wikitable plainrowheaders sortable mw-collapsible mw-collapsed" style="width: 100%;"><caption>'..(args.toggletext or toggletext)..' </caption>'
captionTable = htmlTableLine(captionTable,numberCol,mw.wikibase.getLabel(region),plabel..' '..areaUnits, year, wikidataLink,'header')
end
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double,shape)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
local tw
if double <= 1 then
tw = tonumber(string.match(oWid,"[%.%d]+"))/2 -- shift centre by half the line width
else
tw = tonumber(string.match(oWid,"[%.%d]+")) + (double/2) - 0.5 -- shift centre by a line width + half the gap
end
-- debugmsg(oWid..'+'..double..'/2='..tw)
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2) ).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) - tw).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
local lineAngle=math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi
-- if x1 == x2 then lineAngle=90 end
table.insert(result,"transform: rotate("..lineAngle.."deg);\"></div>")
-- debugmsg('text angle started as '..shape.textNG..' and lineAngle = '..lineAngle)
if (lineAngle < -90) or (lineAngle >90) then lineAngle=lineAngle-180 end
if shape and not shape.textNGarg then shape.textNG=tostring(round(lineAngle,0))..'deg' end -- Put in line angle for a default text angle as well
-- if (y2 >= y1) and (x2 <= x1) then lineAngle=lineAngle-180 debugmsg('arrow angle corrected') end
if x2 < x1 then lineAngle=lineAngle-180 end
if shape and not shape.shapeAngleArg and shape.shape == 'arrowhead' then shape.shapeAngle=tostring(round(lineAngle,0))..'deg' end -- line angle for arrowhead
-- debugmsg('text angle is now '..shape.textNG..'with arrow at '..lineAngle)
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if string.find(shape.shape, 'svgpath,') and #shape.shape > 9 then
pathshape.svgpath=string.sub(shape.shape,9)
shape.shape='svgpath'
end
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if shape.shape == 'arrowhead' then -- enlarge arrowheads, as they only go to the middle of the notional shape-size
w=w*2.5
h=h*2.5
end
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result," scale("..tostring(w/16)..", "..tostring(h/16)..")")
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy+2 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,rndg=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = (w/2)+1,(h/2)+1
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
-- debugmsg('r-(w/2) > widthzone r-w/2='..tostring(r-(w/2))..', widthzone='..widthzone..', label='..(row.labelText or '')..', postype='..(row.posType or ''))
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
-- debugmsg('n-line for '..row.code..':'..tostring(row.gridx+1)..', '..tostring(row.gridy-1)..', '..tostring(row.gridx+row.dx+offsetx)..', '..tostring(row.gridy+row.dy+offsety)..', with width '..tostring(shape.textEW)..'px '..(shape.textES or 'no style')..' and color '..shape.textEC)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap,shape)
-- debugmsg('mark-line line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..(row.mlStyle or 'no style')..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
if color[2] then
propertyTable['stroke-opacity'] = tonumber(color[2] or '20')/100
end
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] and args['map-data-light'] ~= '' then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.4 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
comma=', '
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
local function getQset (qValue,pValue,argNumber,args)
local result={}
local sortType=string.lower(args['geo-list-sort'] or 'a')
local testValue='0'
if sortType == 'alpha' or sortType == 'a' or sortType == 'ah' or sortType == 'al' then
testValue = mw.wikibase.getLabel(qValue)
elseif sortType == 'value' or sortType == 'v' or sortType == 'vh' or sortType == 'vl'then
if qValue==nil or pValue == nil then debugmsg(mw.addWarning((qValue or 'nil')..' Qnumber for '..(pValue or 'nil')))
else
if mw.wikibase.getBestStatements(qValue,pValue)[1] then
testValue = mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.amount or '0'
if pValue == 'P2046' then
testValue = convertToSqKm(testValue, mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.unit)
end
end
end
elseif sortType == 'north' or sortType == 'n' or sortType == 'nh' or sortType == 'nl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.latitude or 0
end
elseif sortType == 'west' or sortType == 'w' or sortType == 'wh' or sortType == 'wl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.longitude or 0
end
end
result[1]=testValue
result[2]=argNumber
return result
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb floatleft">')
else table.insert(result,'<div class="thumb floatright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
--add any boundary Qvalues to map-data-light if requested
if args['geo-region'] and (args['geo-boundaries'] == '1') then
local qmaps=args['map-data-light'] or ''
if qmaps ~='' and string.sub(qmaps,#qmaps) ~= ',' then qmaps=qmaps..',' end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
if mw.wikibase.entityExists(qtemp) then
qmaps=qmaps..qtemp..',' -- see if there is a geocode boundary Qvalue to add
else debugmsg(mw.addWarning('unable to add geocode value for '..mw.wikibase.getLabel..','..qtemp))
end
end
ct=ct+1
end
end
if qmaps and qmaps ~= '' then -- add back the created/expanded list of boundary Qvalues, and remove trailing comma
args['map-data-light'] = string.sub(qmaps,1,#qmaps-1)
end
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
local qset={}
for indx,val in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') or string.match(indx,'geo%-data[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
if string.match(indx,'geo%-data[%d]+') then
local argnum=string.sub(indx,9,#indx) or ''
if args['mark-title'..argnum] == 'none' or (string.find(args['mark-title'..argnum] or 'x','nolist')== 1) then
-- debugmsg('sort values ignored '..argnum..', '..args['mark-title'..argnum])
else
qset[val]=getQset (val, (args['geo-data-type'] or 'P1082'), argnum, args)
end
end
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
local qlist=args['geo-data-list']
if qlist then
if string.sub(qlist,#qlist) ~= ',' then qlist=qlist..',' end
else
qlist=''
end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if (args['find-geo-dots'] or '1') ~= '0' and geoRegion and mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(adminRegion,adminSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
qlist=qlist..qtemp..',' -- add subregion Qvalue to the list
end
ct=ct+1
end
end
if qlist and #qlist>1 then -- create a table from the csv list
local _, c = qlist:gsub(",","")
local qtab=splitItem(qlist,c)
for xd,yd in pairs(qtab) do
yd=string.upper(yd)
if qset[yd] == nil and mw.wikibase.isValidEntityId(yd) then
dotmax=dotmax+1
qset[yd]=getQset (yd, (args['geo-data-type'] or 'P1082'), tostring(dotmax), args)
args['geo-data'..tostring(dotmax)] = yd
-- debugmsg(dotmax..' = '..(string.upper(yd) or 'Q Not found')..': '..(mw.wikibase.getLabel(yd) or 'label Not found')..': '..(mw.wikibase.getSitelink(yd) or 'link Not found'))
elseif not mw.wikibase.isValidEntityId(yd) then
debugmsg(mw.addWarning(yd..' is not a [[wikidata]] page. geo-data-list needs comma-separated Qvalues' ))
else debugmsg(mw.addWarning(yd..' is duplicate'))
end
end
end
-- qset contains set of Qvalues and can be from wikidata geo-region, list of geo-data-list Q values or individual geo-data1= etc
local ts = {} -- table to hold the keys
local useQset=false
for ky, val in pairs(qset) do
table.insert(ts, {key = ky, value = val[1], num=val[2]})
useQset=true
end
if useQset then
local order = string.lower(args['geo-list-sort'] or 'a') -- sort the list from previously stored sortkey, either Lowest or Highest first
if string.find(',al,vl,nl,wl,west,w,alpha,a,' , ','..order..',') then
table.sort(ts, function (a,b) return lessthan(a.value, b.value) end)
else
table.sort(ts, function (a,b) return morethan(a.value, b.value) end)
end
for ky,val in ipairs(ts) do -- assign values from sorted list to numbered parameter
args['numbered'..val.num] = ky -- The numbered parameter becomes the value for each dot that has a geo Qvalue, from manual, list or wiki sources.
end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) or args['geo-data'..x] then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,round(dotitem.lat,5),round(dotitem.lon,5),fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
if captionTable ~= '' and (not args['auto-caption']) then args['auto-caption']='on' end --default to include list if using geo values
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if negativeAnswer[autoOff] then captionTable='' end
if args.caption or (not negativeAnswer[autoOff]) then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then
table.insert(result,args.caption)
if captionTable ~= '' then table.insert(result,'<hr>') end
end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
if captionTable == '' then
capchk=true
else -- add the next line of content to the html table
capchk=false
end
end
end
if captionTable ~= '' then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
for k,v in pairs(captionList) do
local nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
local ngrp=v.value.group
if captionTableText[ngrp] then
-- debugmsg('table text for'..v.value.dotTag..', '..captionTableText[v.value.group][1]..', '..captionTableText[v.value.group][2]..', '..captionTableText[v.value.group][3]..' ('..captionTableText[v.value.group][4]..')')
local c1,c2
c1,c2=checkColors(shapeList[ngrp].shapeColor)
local cDot='<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;'
cDot=cDot..'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div>'
captionTable = htmlTableLine(captionTable,cDot,captionTableText[ngrp][1], captionTableText[ngrp][2], captionTableText[ngrp][3], captionTableText[ngrp][4])
end
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
-- local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<div class = "wikitable mw-collapsible ')
if string.find(autocaption,'collapsed') then table.insert(result,'mw-collapsed') end
table.insert(result,'" style="width:100%; line-height: 17px; border-style:none; text-align: center"><b>'..(args.toggletext or toggletext)..'</b>')
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if captionTable ~= '' then table.insert(result,captionTable..'</table>') end
if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
if maplist.width >= 400 and not(args['show-q-values']) then args['show-q-values'] = '1' -- default to on if wider than 400px
elseif maplist.width < 320 then args['show-q-values'] = '0' end -- turn off Qvalus if frame is too narrow, even if requested
return p._main(args)
end
return p
ab3tfenupaard7gfiwsjw3cdm7zqtxd
204452
204451
2026-07-05T14:42:03Z
HaxelKaszëba
17492
204452
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
local geoTotalPop = 0
local captionTable = ''
local captionTableText = {}
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it may at some point be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
-- -------------- the following text variables are used within the template, and can be translated as required ---------------
-- -------------- you may wish to retain the English version as a comment, to keep track of the original phrase --------------
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext = 'Click for interactive fullscreen map with links to nearby articles'
local toggletext = 'List of numbered map items:'
local termsOfUse = 'Maps: terms of use'
local aboutOSM = 'About OpenStreetMaps'
local yards = 'yds'
local miles = 'miles'
local metres = 'm'
local km = 'km'
local sqkm = 'sq.km'
local year = 'year'
local notFound = 'not found'
local totalItems = 'Total'
local sumOfDatapoints = 'sum of datapoints'
local numberCol = 'No.' -- abbreviated for top of column of code numbers
local wikidataLink = 'Wikidata page'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.semicircle = "M0,5 C0,18.3,20,18.3,20,5 L 0,5z"
pathshape.arrowhead = "M 10,10.2 l 10,2.8 -2,-3 2,-3 -10,2.8 -0.2,0.2z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]') )
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
local t= string.find(label,'wikidata') -- exclude wikidata from tooltip line
if t then
label=string.sub(label,1,t-2) -- don't show wikidata value in tooltip
if string.find(link,'wikidata') then link = '' end -- don't link on click to wikidata (if there was not another link)
end
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r,test)
t.lon = tonumber(t.lon)
t.lat = tonumber(t.lat)
t.lonbase = tonumber(t.lonbase)
t.latbase = tonumber(t.latbase)
t.zoom = tonumber(t.zoom) or 0
if not t.lat then
t.lat = t.latbase or 0
end
local x,y
x = t.width/2 +
(((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)*6378137)) /
(156543.03*math.cos(math.rad(t.latbase))/(2^t.zoom))) *
(1-(0.075*(math.abs(t.latbase)/90)))
y = t.height/2 +
(
(
math.log(math.tan(math.rad(t.latbase)/2 + math.pi/4)) * 6378137
-
math.log(math.tan(math.rad(t.lat)/2 + math.pi/4)) * 6378137
) /
(156543.03*math.cos(math.rad(t.latbase))/(2^t.zoom))
) *
(1-(0.075*(math.abs(t.latbase)/90)))
return round(x,r), round(y,r)
end
local function getScale(zoom, lat, magVal)
zoom = tonumber(zoom) or 0
lat = tonumber(lat)
if not lat then
lat = 0
end
if magVal then
magVal = tonumber(magVal)
if magVal and magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y = round(dist * 10, 1)
return tostring(y * 100) .. metres,
tostring(round(y * 109, 0)) .. yards
elseif dist < 18 then
y = round(dist, 0)
return tostring(y) .. km,
tostring(round(y * 0.621371, 1)) .. miles
elseif dist < 500 then
y = round(dist / 10)
return tostring(y * 10) .. km,
tostring(round(y * 6.21371, 0)) .. miles
else
y = round(dist / 100)
return tostring(y * 100) .. km,
tostring(round(y * 62.1371, 0)) .. miles
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function htmlTableLine (htmlTable,h1,h2,h3,h4,h5,type)
if type == 'header' then
if (h4 or '') ~= 'no-year' then h4='<th class="unsortable">'..h4 or ''..'</th>' else h4 = '' end
if (h5 or '') ~= '' then h5='<th class="unsortable">'..h5..'</th>' else h5 = '' end
return htmlTable..'<tr><th>'..(h1 or '')..'</th><th>'..(h2 or '')..'</th><th>'..(h3 or '')..'</th>'..h4..h5..'</tr>'
else
if (h4 or '') ~= 'no-year' then h4='<td>'..h4 or ''..'</td>' else h4 = '' end
if (h5 or '') ~= '' then h5='<td>'..h5..'</td>' else h5 = '' end
return htmlTable..'<tr><td>'..(h1 or '')..'</td><td>'..(h2 or '')..'</td><td>'..(h3 or '')..'</td>'..h4..h5..'</tr>'
end
end
local function formatInt(number)
if number == 0 then return notFound end
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
int = int:reverse():gsub("(%d%d%d)", "%1,")
return minus .. int:reverse():gsub("^,", "") .. fraction
end
local function convertToSqKm (value,punit)
if string.find(punit, 'Q35852') then value = value / 100 -- convert to sq km if it's hectares
elseif string.find(punit, 'Q232291') then value = value /(1/2.59) -- or this if it's square miles
elseif string.find(punit, 'Q81292') then value = value / 247.1 -- or this if it's acres
end
return value
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
local qvalue=''
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
-- Assign autovalues if a wikidata Qvalue has been supplied
-- some preliminary documentation
-- geo-data-type= default is P1082 (population)
-- other options might include number of households (P1538) and per capita income (P10622) or land area (P2046)
-- geo-data-list= this is a comma-separated list of Q values, for a manually generated list that doesn't require nymbered parameters
-- geo-scalefactor = can enlarge or shrink all the dots in proportion, depending an what the map needs
-- geo-region = needs to be a Qvalue of the larger administrative region that the settlements/sub-regions are in
-- geo-sub-region needs to be a Pvalue, but will default to P150 (some pages use 'has parts' = P527)
-- geo-data3= etc numbered Qvalue item, if used in conjunction with other shape/label parameters
-- shapeD= sets default shape, which migh be n-circle to include numbers.
-- geo-number-size= will give a fixed size for shape numbers. (defaults to 12 when using proportional dots)
-- geo-boundaries= if set to 1 will add any Qvalues for the sub-regions to 'map-data-light'.
-- show-geo-dots= to use proportional dots. Will be fixed size if 0. (set mark-sizeD=0 to have no dot at all, eg to show labels or boundaries)
-- find-geo-dots=1 or 0 (needs to also include geo-region)
-- label-posD = gets additional options: [off] gives no label, [on] will position lower centre,
-- [on+] includes numeric values beside/below label, [on^] will always put it below. [on-] always puts values beside
if args['geo-data'..argval] then -- and (args['mark-title'..argval] or '') ~= 'none' then
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
if not args['geo-number-size'] then args['geo-number-size']=12 end
if not args['label-posD'] then args['label-posD'] = 'on' end
qvalue=args['geo-data'..argval]
-- total poulation is a Qvalue wikidata page entry, or add up all the geo-data values or use a random 10000
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local thisPop=0
local areatxt=''
local yearVal=''
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
thisPop=tonumber(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.amount) or 0
if pvalue == 'P2046' then -- area, so convert from ha to sq km if needed (/100)
thisPop = convertToSqKm(thisPop, mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit)
end
if mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers and mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585 then
yearVal = mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal then yearVal = yearVal:sub(2,5) end -- wikidata reports an entire date and time, so extract the year
end
else
debugmsg(mw.addWarning(mw.wikibase.getLabel(qvalue)..' ('..qvalue..') has no recorded '..plabel))
end
local scalefactor=args['geo-scalefactor'] or 1
if argval=='1' and args['mark-size1'] and not args['mark-sizeD'] then args['mark-sizeD'] = args['mark-size1'] end
if argval=='1' and args['label-pos1'] and not args['label-posD'] then args['label-posD'] = args['label-pos1'] end
if not args['mark-size'..argval] then
if args['show-geo-dots'] == '0' then
args['mark-size'..argval] = args['mark-sizeD'] or '1' -- just use a 'point' for the label to attach to
else
if thisPop>0 then
args['mark-size'..argval] = round(math.sqrt(thisPop/geoTotalPop*100) * 8 * scalefactor,0)
else
args['mark-size'..argval] = args['mark-sizeD'] or '1'
args['shape-outline'..argval]=args['shape-outline'..argval] or args['shape-outlineH']
end
end
end
local posTemp=string.lower(multiCheck(args,'label-pos',argval,'on'))
args['label-pos'..argval] = posTemp
posTemp=splitItem(posTemp,2)[1]
if pvalue == 'P2046' then
thisPop = round(thisPop,2)
areatxt=sqkm..areatxt -- round decimals if sq km area
elseif pvalue == 'P10622' then
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
local punit=(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit or ''):match("Q.*") -- extract Q value
if punit == 'Q4917' then areatxt=' US$'
elseif punit == 'Q4916' then areatxt=' euro'
elseif punit == 'Q25224' then areatxt=' £GBP'
else areatxt=mw.wikibase.getLabel(punit)..areatxt
end
end
end
if posTemp=='off' then
else
if not args['label'..argval] then args['label'..argval] = mw.wikibase.getLabel(qvalue) end
local tline=posTemp:find("[%+%^%-]")
if tline then
local tval=posTemp:sub(tline,tline)
local eline
posTemp=posTemp:sub(1,tline-1)
if posTemp == 'left' or posTemp == 'right' or string.find(posTemp,'east') or string.find(posTemp,'west') then
if tval == '^' then eline = '^' else eline = ' ' end -- only add line break if 'forced', used for left/right
elseif tval == '+' or tval == '^' then eline = '^' -- add linebreak unless 'forced' not to
else eline = ' ' -- must be a dash, so no line break
end
args['label'..argval] = args['label'..argval]..eline..'('..formatInt(thisPop)..')'
args['label-pos'..argval] = posTemp..args['label-pos'..argval]:sub(tline+1,#args['label-pos'..argval])
end
end
if posTemp=='on' then
if argval=='1' and (not args.ldyD) then args.ldyD = args['ldy'..argval] or '0' end
if not args['ldy'..argval] then args['ldy'..argval] = args.ldyD end -- ensure ldy for that dot is added to the position
args['label-pos'..argval]='center'
local _,br = string.gsub(args['label'..argval],'%^','')
local item=splitItem(args['label-size'..argval],2)
args['ldy'..argval] = (args['ldy'..argval] or 0) + ((item[1] or 11) * ((br+1)*0.6)) + ((args['geo-number-size'] or 12) / 2)
end
local nt=''
if args['mark-title'..argval] == 'nolist' then nt='nolist,' end
if not args['mark-title'..argval] or args['mark-title'..argval] == '' or args['mark-title'..argval] == 'nolist' then
if mw.wikibase.getSitelink(qvalue) then
args['mark-title'..argval] = nt..'[['..mw.wikibase.getSitelink(qvalue)..']]'
else
args['mark-title'..argval] = nt..mw.wikibase.getLabel(qvalue)
end
end -- If mark-title is not set locally then attempt Sitelink value, or use label
if thisPop == 0 then areatxt='' end
captionTableText[argval]={}
captionTableText[argval][1]= args['mark-title'..argval]
captionTableText[argval][2]= formatInt(thisPop)
if maplist.width >= 280 then captionTableText[argval][3]= yearVal else captionTableText[argval][3]='no-year' end
if args['show-q-values'] == '1' then
captionTableText[argval][4]='<small>[[wikidata:'..qvalue..'|'..qvalue..']]</small>'
else
local nbsp = ' '
if maplist.width < 230 then nbsp = ' ' end
captionTableText[argval][4]=''
if captionTableText[argval][3] == 'no-year' then
captionTableText[argval][2] = captionTableText[argval][2]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
else
captionTableText[argval][3] = captionTableText[argval][3]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
end
end
if args['mark-title'..argval] == 'none' then
-- no title
else
args['mark-title'..argval] = args['mark-title'..argval]..' ('..plabel..' '..formatInt(thisPop)..areatxt..')'
end
if not args['mark-description'..argval] then args['mark-description'..argval] = (mw.wikibase.getDescription(qvalue) or '')..'<br>' end
if (not args['mark-image'..argval]) and (mw.wikibase.getBestStatements(qvalue,'P18')[1]) then
args['mark-image'..argval] = mw.wikibase.getBestStatements(qvalue,'P18')[1].mainsnak.datavalue.value
end
item = multiCheck(args,'shape',argval,'circle') --default to circle if geo-data
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
else
item = multiCheck(args,'shape',argval,'image')
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
end
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,(shapeResult[nval].shapeSize or '14px')) -- add geo-data as fallback if already assigned
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
if args['shape-angle'..argval] then shapeResult[nval].shapeAngleArg=true else shapeResult[nval].shapeAngleArg=false end
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
shapeResult[nval].textAT=args['label-attribute'..argval] or ''
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
if args['label-angle'..argval] then shapeResult[nval].textNGarg=true else shapeResult[nval].textNGarg=false end
--sgd=dotTag attributes
shapeResult[nval].tagSize=(args['geo-number-size'] or tostring(shapeHeight*1.5))..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
if itemTab[1] == nil or itemTab[2] == nil then
debugmsg(mw.addWarning('Unable to read coordinates for mark-coord'..argval))
else
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
end
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
if string.find(item,'nolist') then dotItem.param1='nolist,' item = string.sub(args['mark-title'..argval],8) or args['label'..argval] end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
-- debugmsg('dlink for '..item..' is '..dotItem.dlink)
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
-- debugmsg(dotItem.code..' = '..dotItem.lat..','..dotItem.lon..': '..dotItem.labelText..' - '..dotItem.title..', '..shapeResult[nval].shapeSize)
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['geo-data'..x] and (not args['mark-coord'..x]) and (not args['mark-lat'..x]) then
-- get lat and lon values from wikidata and put them into the args.lat and args.lon for use wherever needed
if (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1]) then
args['mark-lat'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.latitude) or '0'
args['mark-lon'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.longitude) or '0'
local p=args['geo-data-type'] or 'P1082'
if mw.wikibase.getBestStatements(args['geo-data'..x],p)[1] then
local n = tonumber(mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.amount) or 0
if p == 'P2046' then -- area, so convert to sq km if needed (/100)
n = convertToSqKm( n, mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.unit)
end
geoTotalPop = geoTotalPop + n
end
else debugmsg(mw.addWarning('could not find coords for '..mw.wikibase.getLabel(args['geo-data'..x])..', '..args['geo-data'..x]))
end
end
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form (needs a second table, seemingly)
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
local unitConvert=1
local areaUnits=''
if args['geo-region'] and (args['find-geo-dots'] or '1') ~= '0' then
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
local subhead='' -- Assemble the subhead content, a line of text about the region as a whole
if region and mw.wikibase.getBestStatements(region,pvalue)[1] then
local punit=mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.unit:match("Q.*") -- extract Q value
if pvalue == 'P10622' then -- add other currency-based pvalues here
areaUnits=mw.wikibase.getLabel(punit)
end
if punit == 'Q35852' then unitConvert=100 areaUnits = ' '..sqkm -- if its hectares, convert to sq km
elseif punit == 'Q232291' then unitConvert = (1/2.59) areaUnits = ' '..sqkm -- or this if it's square miles
elseif punit == 'Q81292' then unitConvert = 247.1 areaUnits = ' '..sqkm -- or this if it's acres
elseif punit == 'Q712226' then unitConvert = 1 areaUnits = ' '..sqkm -- or this if it's already sq km
elseif punit == 'Q4917' then areaUnits=' US$' -- replace long version with some shortform currencies ()
elseif punit == 'Q4916' then areaUnits=' euro'
elseif punit == 'Q25224' then areaUnits=' £GBP'
end
-- if pvalue == 'P2046' then
-- may need to add other instances of sq km so add in other P-items here if they need units showing
-- end
geoTotalPop=(mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.amount) / unitConvert
local yearVal
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers then
yearVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal and yearVal:sub(1,1) == '+' then yearVal = yearVal:sub(2,5)
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459 and mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1] then
local methodVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1].datavalue.value.id
if methodVal then yearVal=yearVal..' '..mw.wikibase.getLabel(methodVal) end
end
yearVal = ' ('..yearVal..')'
else yearVal =''
end
end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ([[wikidata:'..region..']]) = '..formatInt(round(geoTotalPop),1)..areaUnits..yearVal
else
if pvalue == 'P2046' then areaUnits = sqkm end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ('..sumOfDatapoints..') = '..formatInt(round(geoTotalPop),1)..areaUnits
end
if maplist.width < 350 and #plabel > 4 then plabel=string.sub((plabel or ' '),1,3)..'.' end
if maplist.width < 330 then numberCol = ' ' end
if maplist.width < 280 then year = 'no-year' end
captionTable = subhead
if (args['show-q-values'] or '') ~= '1' then
captionTable = captionTable..'<div style="float:right;"><small> (WD = '..wikidataLink..')</small></div><br>'
wikidataLink=''
end
captionTable = captionTable..'<table class="wikitable plainrowheaders sortable mw-collapsible mw-collapsed" style="width: 100%;"><caption>'..(args.toggletext or toggletext)..' </caption>'
captionTable = htmlTableLine(captionTable,numberCol,mw.wikibase.getLabel(region),plabel..' '..areaUnits, year, wikidataLink,'header')
end
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double,shape)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
local tw
if double <= 1 then
tw = tonumber(string.match(oWid,"[%.%d]+"))/2 -- shift centre by half the line width
else
tw = tonumber(string.match(oWid,"[%.%d]+")) + (double/2) - 0.5 -- shift centre by a line width + half the gap
end
-- debugmsg(oWid..'+'..double..'/2='..tw)
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2) ).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) - tw).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
local lineAngle=math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi
-- if x1 == x2 then lineAngle=90 end
table.insert(result,"transform: rotate("..lineAngle.."deg);\"></div>")
-- debugmsg('text angle started as '..shape.textNG..' and lineAngle = '..lineAngle)
if (lineAngle < -90) or (lineAngle >90) then lineAngle=lineAngle-180 end
if shape and not shape.textNGarg then shape.textNG=tostring(round(lineAngle,0))..'deg' end -- Put in line angle for a default text angle as well
-- if (y2 >= y1) and (x2 <= x1) then lineAngle=lineAngle-180 debugmsg('arrow angle corrected') end
if x2 < x1 then lineAngle=lineAngle-180 end
if shape and not shape.shapeAngleArg and shape.shape == 'arrowhead' then shape.shapeAngle=tostring(round(lineAngle,0))..'deg' end -- line angle for arrowhead
-- debugmsg('text angle is now '..shape.textNG..'with arrow at '..lineAngle)
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if string.find(shape.shape, 'svgpath,') and #shape.shape > 9 then
pathshape.svgpath=string.sub(shape.shape,9)
shape.shape='svgpath'
end
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if shape.shape == 'arrowhead' then -- enlarge arrowheads, as they only go to the middle of the notional shape-size
w=w*2.5
h=h*2.5
end
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result," scale("..tostring(w/16)..", "..tostring(h/16)..")")
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy+2 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,rndg=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = (w/2)+1,(h/2)+1
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
-- debugmsg('r-(w/2) > widthzone r-w/2='..tostring(r-(w/2))..', widthzone='..widthzone..', label='..(row.labelText or '')..', postype='..(row.posType or ''))
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
-- debugmsg('n-line for '..row.code..':'..tostring(row.gridx+1)..', '..tostring(row.gridy-1)..', '..tostring(row.gridx+row.dx+offsetx)..', '..tostring(row.gridy+row.dy+offsety)..', with width '..tostring(shape.textEW)..'px '..(shape.textES or 'no style')..' and color '..shape.textEC)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap,shape)
-- debugmsg('mark-line line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..(row.mlStyle or 'no style')..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
if color[2] then
propertyTable['stroke-opacity'] = tonumber(color[2] or '20')/100
end
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] and args['map-data-light'] ~= '' then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.4 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
comma=', '
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
local function getQset (qValue,pValue,argNumber,args)
local result={}
local sortType=string.lower(args['geo-list-sort'] or 'a')
local testValue='0'
if sortType == 'alpha' or sortType == 'a' or sortType == 'ah' or sortType == 'al' then
testValue = mw.wikibase.getLabel(qValue)
elseif sortType == 'value' or sortType == 'v' or sortType == 'vh' or sortType == 'vl'then
if qValue==nil or pValue == nil then debugmsg(mw.addWarning((qValue or 'nil')..' Qnumber for '..(pValue or 'nil')))
else
if mw.wikibase.getBestStatements(qValue,pValue)[1] then
testValue = mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.amount or '0'
if pValue == 'P2046' then
testValue = convertToSqKm(testValue, mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.unit)
end
end
end
elseif sortType == 'north' or sortType == 'n' or sortType == 'nh' or sortType == 'nl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.latitude or 0
end
elseif sortType == 'west' or sortType == 'w' or sortType == 'wh' or sortType == 'wl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.longitude or 0
end
end
result[1]=testValue
result[2]=argNumber
return result
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb floatleft">')
else table.insert(result,'<div class="thumb floatright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
--add any boundary Qvalues to map-data-light if requested
if args['geo-region'] and (args['geo-boundaries'] == '1') then
local qmaps=args['map-data-light'] or ''
if qmaps ~='' and string.sub(qmaps,#qmaps) ~= ',' then qmaps=qmaps..',' end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
if mw.wikibase.entityExists(qtemp) then
qmaps=qmaps..qtemp..',' -- see if there is a geocode boundary Qvalue to add
else debugmsg(mw.addWarning('unable to add geocode value for '..mw.wikibase.getLabel..','..qtemp))
end
end
ct=ct+1
end
end
if qmaps and qmaps ~= '' then -- add back the created/expanded list of boundary Qvalues, and remove trailing comma
args['map-data-light'] = string.sub(qmaps,1,#qmaps-1)
end
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
local qset={}
for indx,val in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') or string.match(indx,'geo%-data[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
if string.match(indx,'geo%-data[%d]+') then
local argnum=string.sub(indx,9,#indx) or ''
if args['mark-title'..argnum] == 'none' or (string.find(args['mark-title'..argnum] or 'x','nolist')== 1) then
-- debugmsg('sort values ignored '..argnum..', '..args['mark-title'..argnum])
else
qset[val]=getQset (val, (args['geo-data-type'] or 'P1082'), argnum, args)
end
end
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
local qlist=args['geo-data-list']
if qlist then
if string.sub(qlist,#qlist) ~= ',' then qlist=qlist..',' end
else
qlist=''
end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if (args['find-geo-dots'] or '1') ~= '0' and geoRegion and mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(adminRegion,adminSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
qlist=qlist..qtemp..',' -- add subregion Qvalue to the list
end
ct=ct+1
end
end
if qlist and #qlist>1 then -- create a table from the csv list
local _, c = qlist:gsub(",","")
local qtab=splitItem(qlist,c)
for xd,yd in pairs(qtab) do
yd=string.upper(yd)
if qset[yd] == nil and mw.wikibase.isValidEntityId(yd) then
dotmax=dotmax+1
qset[yd]=getQset (yd, (args['geo-data-type'] or 'P1082'), tostring(dotmax), args)
args['geo-data'..tostring(dotmax)] = yd
-- debugmsg(dotmax..' = '..(string.upper(yd) or 'Q Not found')..': '..(mw.wikibase.getLabel(yd) or 'label Not found')..': '..(mw.wikibase.getSitelink(yd) or 'link Not found'))
elseif not mw.wikibase.isValidEntityId(yd) then
debugmsg(mw.addWarning(yd..' is not a [[wikidata]] page. geo-data-list needs comma-separated Qvalues' ))
else debugmsg(mw.addWarning(yd..' is duplicate'))
end
end
end
-- qset contains set of Qvalues and can be from wikidata geo-region, list of geo-data-list Q values or individual geo-data1= etc
local ts = {} -- table to hold the keys
local useQset=false
for ky, val in pairs(qset) do
table.insert(ts, {key = ky, value = val[1], num=val[2]})
useQset=true
end
if useQset then
local order = string.lower(args['geo-list-sort'] or 'a') -- sort the list from previously stored sortkey, either Lowest or Highest first
if string.find(',al,vl,nl,wl,west,w,alpha,a,' , ','..order..',') then
table.sort(ts, function (a,b) return lessthan(a.value, b.value) end)
else
table.sort(ts, function (a,b) return morethan(a.value, b.value) end)
end
for ky,val in ipairs(ts) do -- assign values from sorted list to numbered parameter
args['numbered'..val.num] = ky -- The numbered parameter becomes the value for each dot that has a geo Qvalue, from manual, list or wiki sources.
end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) or args['geo-data'..x] then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,round(dotitem.lat,5),round(dotitem.lon,5),fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
if captionTable ~= '' and (not args['auto-caption']) then args['auto-caption']='on' end --default to include list if using geo values
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if negativeAnswer[autoOff] then captionTable='' end
if args.caption or (not negativeAnswer[autoOff]) then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then
table.insert(result,args.caption)
if captionTable ~= '' then table.insert(result,'<hr>') end
end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
if captionTable == '' then
capchk=true
else -- add the next line of content to the html table
capchk=false
end
end
end
if captionTable ~= '' then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
for k,v in pairs(captionList) do
local nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
local ngrp=v.value.group
if captionTableText[ngrp] then
-- debugmsg('table text for'..v.value.dotTag..', '..captionTableText[v.value.group][1]..', '..captionTableText[v.value.group][2]..', '..captionTableText[v.value.group][3]..' ('..captionTableText[v.value.group][4]..')')
local c1,c2
c1,c2=checkColors(shapeList[ngrp].shapeColor)
local cDot='<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;'
cDot=cDot..'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div>'
captionTable = htmlTableLine(captionTable,cDot,captionTableText[ngrp][1], captionTableText[ngrp][2], captionTableText[ngrp][3], captionTableText[ngrp][4])
end
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
-- local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<div class = "wikitable mw-collapsible ')
if string.find(autocaption,'collapsed') then table.insert(result,'mw-collapsed') end
table.insert(result,'" style="width:100%; line-height: 17px; border-style:none; text-align: center"><b>'..(args.toggletext or toggletext)..'</b>')
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if captionTable ~= '' then table.insert(result,captionTable..'</table>') end
if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
if maplist.width >= 400 and not(args['show-q-values']) then args['show-q-values'] = '1' -- default to on if wider than 400px
elseif maplist.width < 320 then args['show-q-values'] = '0' end -- turn off Qvalus if frame is too narrow, even if requested
return p._main(args)
end
return p
3d1rwotiu723z91tf01uwt6q04ej1me
204453
204452
2026-07-05T14:44:14Z
HaxelKaszëba
17492
204453
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
local geoTotalPop = 0
local captionTable = ''
local captionTableText = {}
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it may at some point be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
-- -------------- the following text variables are used within the template, and can be translated as required ---------------
-- -------------- you may wish to retain the English version as a comment, to keep track of the original phrase --------------
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext = 'Click for interactive fullscreen map with links to nearby articles'
local toggletext = 'List of numbered map items:'
local termsOfUse = 'Maps: terms of use'
local aboutOSM = 'About OpenStreetMaps'
local yards = 'yds'
local miles = 'miles'
local metres = 'm'
local km = 'km'
local sqkm = 'sq.km'
local year = 'year'
local notFound = 'not found'
local totalItems = 'Total'
local sumOfDatapoints = 'sum of datapoints'
local numberCol = 'No.' -- abbreviated for top of column of code numbers
local wikidataLink = 'Wikidata page'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.semicircle = "M0,5 C0,18.3,20,18.3,20,5 L 0,5z"
pathshape.arrowhead = "M 10,10.2 l 10,2.8 -2,-3 2,-3 -10,2.8 -0.2,0.2z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]') )
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
local t= string.find(label,'wikidata') -- exclude wikidata from tooltip line
if t then
label=string.sub(label,1,t-2) -- don't show wikidata value in tooltip
if string.find(link,'wikidata') then link = '' end -- don't link on click to wikidata (if there was not another link)
end
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t, r, test)
t.width = tonumber(t.width) or 0
t.height = tonumber(t.height) or 0
t.lon = tonumber(t.lon)
t.lat = tonumber(t.lat)
t.lonbase = tonumber(t.lonbase)
t.latbase = tonumber(t.latbase)
t.zoom = tonumber(t.zoom) or 0
if not t.lon or not t.lat or not t.lonbase or not t.latbase then
error(
"maptogrid: missing values → " ..
"lon=" .. tostring(t.lon) ..
" lat=" .. tostring(t.lat) ..
" lonbase=" .. tostring(t.lonbase) ..
" latbase=" .. tostring(t.latbase)
)
end
local zoom = t.zoom
local x = t.width / 2 +
(
(
(math.rad(t.lon) * 6378137) -
(math.rad(t.lonbase) * 6378137)
)
/
(156543.03 * math.cos(math.rad(t.latbase)) / (2 ^ zoom))
)
* (1 - (0.075 * (math.abs(t.latbase) / 90)))
local y = t.height / 2 +
(
(
(math.log(math.tan(math.rad(t.latbase) / 2 + math.pi / 4)) * 6378137)
-
(math.log(math.tan(math.rad(t.lat) / 2 + math.pi / 4)) * 6378137)
)
/
(156543.03 * math.cos(math.rad(t.latbase)) / (2 ^ zoom))
)
* (1 - (0.075 * (math.abs(t.latbase) / 90)))
return round(x, r), round(y, r)
end
local function getScale(zoom, lat, magVal)
zoom = tonumber(zoom) or 0
lat = tonumber(lat)
if not lat then
lat = 0
end
if magVal then
magVal = tonumber(magVal)
if magVal and magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y = round(dist * 10, 1)
return tostring(y * 100) .. metres,
tostring(round(y * 109, 0)) .. yards
elseif dist < 18 then
y = round(dist, 0)
return tostring(y) .. km,
tostring(round(y * 0.621371, 1)) .. miles
elseif dist < 500 then
y = round(dist / 10)
return tostring(y * 10) .. km,
tostring(round(y * 6.21371, 0)) .. miles
else
y = round(dist / 100)
return tostring(y * 100) .. km,
tostring(round(y * 62.1371, 0)) .. miles
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function htmlTableLine (htmlTable,h1,h2,h3,h4,h5,type)
if type == 'header' then
if (h4 or '') ~= 'no-year' then h4='<th class="unsortable">'..h4 or ''..'</th>' else h4 = '' end
if (h5 or '') ~= '' then h5='<th class="unsortable">'..h5..'</th>' else h5 = '' end
return htmlTable..'<tr><th>'..(h1 or '')..'</th><th>'..(h2 or '')..'</th><th>'..(h3 or '')..'</th>'..h4..h5..'</tr>'
else
if (h4 or '') ~= 'no-year' then h4='<td>'..h4 or ''..'</td>' else h4 = '' end
if (h5 or '') ~= '' then h5='<td>'..h5..'</td>' else h5 = '' end
return htmlTable..'<tr><td>'..(h1 or '')..'</td><td>'..(h2 or '')..'</td><td>'..(h3 or '')..'</td>'..h4..h5..'</tr>'
end
end
local function formatInt(number)
if number == 0 then return notFound end
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
int = int:reverse():gsub("(%d%d%d)", "%1,")
return minus .. int:reverse():gsub("^,", "") .. fraction
end
local function convertToSqKm (value,punit)
if string.find(punit, 'Q35852') then value = value / 100 -- convert to sq km if it's hectares
elseif string.find(punit, 'Q232291') then value = value /(1/2.59) -- or this if it's square miles
elseif string.find(punit, 'Q81292') then value = value / 247.1 -- or this if it's acres
end
return value
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
local qvalue=''
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
-- Assign autovalues if a wikidata Qvalue has been supplied
-- some preliminary documentation
-- geo-data-type= default is P1082 (population)
-- other options might include number of households (P1538) and per capita income (P10622) or land area (P2046)
-- geo-data-list= this is a comma-separated list of Q values, for a manually generated list that doesn't require nymbered parameters
-- geo-scalefactor = can enlarge or shrink all the dots in proportion, depending an what the map needs
-- geo-region = needs to be a Qvalue of the larger administrative region that the settlements/sub-regions are in
-- geo-sub-region needs to be a Pvalue, but will default to P150 (some pages use 'has parts' = P527)
-- geo-data3= etc numbered Qvalue item, if used in conjunction with other shape/label parameters
-- shapeD= sets default shape, which migh be n-circle to include numbers.
-- geo-number-size= will give a fixed size for shape numbers. (defaults to 12 when using proportional dots)
-- geo-boundaries= if set to 1 will add any Qvalues for the sub-regions to 'map-data-light'.
-- show-geo-dots= to use proportional dots. Will be fixed size if 0. (set mark-sizeD=0 to have no dot at all, eg to show labels or boundaries)
-- find-geo-dots=1 or 0 (needs to also include geo-region)
-- label-posD = gets additional options: [off] gives no label, [on] will position lower centre,
-- [on+] includes numeric values beside/below label, [on^] will always put it below. [on-] always puts values beside
if args['geo-data'..argval] then -- and (args['mark-title'..argval] or '') ~= 'none' then
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
if not args['geo-number-size'] then args['geo-number-size']=12 end
if not args['label-posD'] then args['label-posD'] = 'on' end
qvalue=args['geo-data'..argval]
-- total poulation is a Qvalue wikidata page entry, or add up all the geo-data values or use a random 10000
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local thisPop=0
local areatxt=''
local yearVal=''
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
thisPop=tonumber(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.amount) or 0
if pvalue == 'P2046' then -- area, so convert from ha to sq km if needed (/100)
thisPop = convertToSqKm(thisPop, mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit)
end
if mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers and mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585 then
yearVal = mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal then yearVal = yearVal:sub(2,5) end -- wikidata reports an entire date and time, so extract the year
end
else
debugmsg(mw.addWarning(mw.wikibase.getLabel(qvalue)..' ('..qvalue..') has no recorded '..plabel))
end
local scalefactor=args['geo-scalefactor'] or 1
if argval=='1' and args['mark-size1'] and not args['mark-sizeD'] then args['mark-sizeD'] = args['mark-size1'] end
if argval=='1' and args['label-pos1'] and not args['label-posD'] then args['label-posD'] = args['label-pos1'] end
if not args['mark-size'..argval] then
if args['show-geo-dots'] == '0' then
args['mark-size'..argval] = args['mark-sizeD'] or '1' -- just use a 'point' for the label to attach to
else
if thisPop>0 then
args['mark-size'..argval] = round(math.sqrt(thisPop/geoTotalPop*100) * 8 * scalefactor,0)
else
args['mark-size'..argval] = args['mark-sizeD'] or '1'
args['shape-outline'..argval]=args['shape-outline'..argval] or args['shape-outlineH']
end
end
end
local posTemp=string.lower(multiCheck(args,'label-pos',argval,'on'))
args['label-pos'..argval] = posTemp
posTemp=splitItem(posTemp,2)[1]
if pvalue == 'P2046' then
thisPop = round(thisPop,2)
areatxt=sqkm..areatxt -- round decimals if sq km area
elseif pvalue == 'P10622' then
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
local punit=(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit or ''):match("Q.*") -- extract Q value
if punit == 'Q4917' then areatxt=' US$'
elseif punit == 'Q4916' then areatxt=' euro'
elseif punit == 'Q25224' then areatxt=' £GBP'
else areatxt=mw.wikibase.getLabel(punit)..areatxt
end
end
end
if posTemp=='off' then
else
if not args['label'..argval] then args['label'..argval] = mw.wikibase.getLabel(qvalue) end
local tline=posTemp:find("[%+%^%-]")
if tline then
local tval=posTemp:sub(tline,tline)
local eline
posTemp=posTemp:sub(1,tline-1)
if posTemp == 'left' or posTemp == 'right' or string.find(posTemp,'east') or string.find(posTemp,'west') then
if tval == '^' then eline = '^' else eline = ' ' end -- only add line break if 'forced', used for left/right
elseif tval == '+' or tval == '^' then eline = '^' -- add linebreak unless 'forced' not to
else eline = ' ' -- must be a dash, so no line break
end
args['label'..argval] = args['label'..argval]..eline..'('..formatInt(thisPop)..')'
args['label-pos'..argval] = posTemp..args['label-pos'..argval]:sub(tline+1,#args['label-pos'..argval])
end
end
if posTemp=='on' then
if argval=='1' and (not args.ldyD) then args.ldyD = args['ldy'..argval] or '0' end
if not args['ldy'..argval] then args['ldy'..argval] = args.ldyD end -- ensure ldy for that dot is added to the position
args['label-pos'..argval]='center'
local _,br = string.gsub(args['label'..argval],'%^','')
local item=splitItem(args['label-size'..argval],2)
args['ldy'..argval] = (args['ldy'..argval] or 0) + ((item[1] or 11) * ((br+1)*0.6)) + ((args['geo-number-size'] or 12) / 2)
end
local nt=''
if args['mark-title'..argval] == 'nolist' then nt='nolist,' end
if not args['mark-title'..argval] or args['mark-title'..argval] == '' or args['mark-title'..argval] == 'nolist' then
if mw.wikibase.getSitelink(qvalue) then
args['mark-title'..argval] = nt..'[['..mw.wikibase.getSitelink(qvalue)..']]'
else
args['mark-title'..argval] = nt..mw.wikibase.getLabel(qvalue)
end
end -- If mark-title is not set locally then attempt Sitelink value, or use label
if thisPop == 0 then areatxt='' end
captionTableText[argval]={}
captionTableText[argval][1]= args['mark-title'..argval]
captionTableText[argval][2]= formatInt(thisPop)
if maplist.width >= 280 then captionTableText[argval][3]= yearVal else captionTableText[argval][3]='no-year' end
if args['show-q-values'] == '1' then
captionTableText[argval][4]='<small>[[wikidata:'..qvalue..'|'..qvalue..']]</small>'
else
local nbsp = ' '
if maplist.width < 230 then nbsp = ' ' end
captionTableText[argval][4]=''
if captionTableText[argval][3] == 'no-year' then
captionTableText[argval][2] = captionTableText[argval][2]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
else
captionTableText[argval][3] = captionTableText[argval][3]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
end
end
if args['mark-title'..argval] == 'none' then
-- no title
else
args['mark-title'..argval] = args['mark-title'..argval]..' ('..plabel..' '..formatInt(thisPop)..areatxt..')'
end
if not args['mark-description'..argval] then args['mark-description'..argval] = (mw.wikibase.getDescription(qvalue) or '')..'<br>' end
if (not args['mark-image'..argval]) and (mw.wikibase.getBestStatements(qvalue,'P18')[1]) then
args['mark-image'..argval] = mw.wikibase.getBestStatements(qvalue,'P18')[1].mainsnak.datavalue.value
end
item = multiCheck(args,'shape',argval,'circle') --default to circle if geo-data
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
else
item = multiCheck(args,'shape',argval,'image')
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
end
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,(shapeResult[nval].shapeSize or '14px')) -- add geo-data as fallback if already assigned
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
if args['shape-angle'..argval] then shapeResult[nval].shapeAngleArg=true else shapeResult[nval].shapeAngleArg=false end
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
shapeResult[nval].textAT=args['label-attribute'..argval] or ''
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
if args['label-angle'..argval] then shapeResult[nval].textNGarg=true else shapeResult[nval].textNGarg=false end
--sgd=dotTag attributes
shapeResult[nval].tagSize=(args['geo-number-size'] or tostring(shapeHeight*1.5))..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
if itemTab[1] == nil or itemTab[2] == nil then
debugmsg(mw.addWarning('Unable to read coordinates for mark-coord'..argval))
else
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
end
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
if string.find(item,'nolist') then dotItem.param1='nolist,' item = string.sub(args['mark-title'..argval],8) or args['label'..argval] end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
-- debugmsg('dlink for '..item..' is '..dotItem.dlink)
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
-- debugmsg(dotItem.code..' = '..dotItem.lat..','..dotItem.lon..': '..dotItem.labelText..' - '..dotItem.title..', '..shapeResult[nval].shapeSize)
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['geo-data'..x] and (not args['mark-coord'..x]) and (not args['mark-lat'..x]) then
-- get lat and lon values from wikidata and put them into the args.lat and args.lon for use wherever needed
if (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1]) then
args['mark-lat'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.latitude) or '0'
args['mark-lon'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.longitude) or '0'
local p=args['geo-data-type'] or 'P1082'
if mw.wikibase.getBestStatements(args['geo-data'..x],p)[1] then
local n = tonumber(mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.amount) or 0
if p == 'P2046' then -- area, so convert to sq km if needed (/100)
n = convertToSqKm( n, mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.unit)
end
geoTotalPop = geoTotalPop + n
end
else debugmsg(mw.addWarning('could not find coords for '..mw.wikibase.getLabel(args['geo-data'..x])..', '..args['geo-data'..x]))
end
end
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form (needs a second table, seemingly)
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
local unitConvert=1
local areaUnits=''
if args['geo-region'] and (args['find-geo-dots'] or '1') ~= '0' then
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
local subhead='' -- Assemble the subhead content, a line of text about the region as a whole
if region and mw.wikibase.getBestStatements(region,pvalue)[1] then
local punit=mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.unit:match("Q.*") -- extract Q value
if pvalue == 'P10622' then -- add other currency-based pvalues here
areaUnits=mw.wikibase.getLabel(punit)
end
if punit == 'Q35852' then unitConvert=100 areaUnits = ' '..sqkm -- if its hectares, convert to sq km
elseif punit == 'Q232291' then unitConvert = (1/2.59) areaUnits = ' '..sqkm -- or this if it's square miles
elseif punit == 'Q81292' then unitConvert = 247.1 areaUnits = ' '..sqkm -- or this if it's acres
elseif punit == 'Q712226' then unitConvert = 1 areaUnits = ' '..sqkm -- or this if it's already sq km
elseif punit == 'Q4917' then areaUnits=' US$' -- replace long version with some shortform currencies ()
elseif punit == 'Q4916' then areaUnits=' euro'
elseif punit == 'Q25224' then areaUnits=' £GBP'
end
-- if pvalue == 'P2046' then
-- may need to add other instances of sq km so add in other P-items here if they need units showing
-- end
geoTotalPop=(mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.amount) / unitConvert
local yearVal
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers then
yearVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal and yearVal:sub(1,1) == '+' then yearVal = yearVal:sub(2,5)
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459 and mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1] then
local methodVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1].datavalue.value.id
if methodVal then yearVal=yearVal..' '..mw.wikibase.getLabel(methodVal) end
end
yearVal = ' ('..yearVal..')'
else yearVal =''
end
end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ([[wikidata:'..region..']]) = '..formatInt(round(geoTotalPop),1)..areaUnits..yearVal
else
if pvalue == 'P2046' then areaUnits = sqkm end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ('..sumOfDatapoints..') = '..formatInt(round(geoTotalPop),1)..areaUnits
end
if maplist.width < 350 and #plabel > 4 then plabel=string.sub((plabel or ' '),1,3)..'.' end
if maplist.width < 330 then numberCol = ' ' end
if maplist.width < 280 then year = 'no-year' end
captionTable = subhead
if (args['show-q-values'] or '') ~= '1' then
captionTable = captionTable..'<div style="float:right;"><small> (WD = '..wikidataLink..')</small></div><br>'
wikidataLink=''
end
captionTable = captionTable..'<table class="wikitable plainrowheaders sortable mw-collapsible mw-collapsed" style="width: 100%;"><caption>'..(args.toggletext or toggletext)..' </caption>'
captionTable = htmlTableLine(captionTable,numberCol,mw.wikibase.getLabel(region),plabel..' '..areaUnits, year, wikidataLink,'header')
end
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double,shape)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
local tw
if double <= 1 then
tw = tonumber(string.match(oWid,"[%.%d]+"))/2 -- shift centre by half the line width
else
tw = tonumber(string.match(oWid,"[%.%d]+")) + (double/2) - 0.5 -- shift centre by a line width + half the gap
end
-- debugmsg(oWid..'+'..double..'/2='..tw)
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2) ).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) - tw).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
local lineAngle=math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi
-- if x1 == x2 then lineAngle=90 end
table.insert(result,"transform: rotate("..lineAngle.."deg);\"></div>")
-- debugmsg('text angle started as '..shape.textNG..' and lineAngle = '..lineAngle)
if (lineAngle < -90) or (lineAngle >90) then lineAngle=lineAngle-180 end
if shape and not shape.textNGarg then shape.textNG=tostring(round(lineAngle,0))..'deg' end -- Put in line angle for a default text angle as well
-- if (y2 >= y1) and (x2 <= x1) then lineAngle=lineAngle-180 debugmsg('arrow angle corrected') end
if x2 < x1 then lineAngle=lineAngle-180 end
if shape and not shape.shapeAngleArg and shape.shape == 'arrowhead' then shape.shapeAngle=tostring(round(lineAngle,0))..'deg' end -- line angle for arrowhead
-- debugmsg('text angle is now '..shape.textNG..'with arrow at '..lineAngle)
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if string.find(shape.shape, 'svgpath,') and #shape.shape > 9 then
pathshape.svgpath=string.sub(shape.shape,9)
shape.shape='svgpath'
end
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if shape.shape == 'arrowhead' then -- enlarge arrowheads, as they only go to the middle of the notional shape-size
w=w*2.5
h=h*2.5
end
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result," scale("..tostring(w/16)..", "..tostring(h/16)..")")
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy+2 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,rndg=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = (w/2)+1,(h/2)+1
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
-- debugmsg('r-(w/2) > widthzone r-w/2='..tostring(r-(w/2))..', widthzone='..widthzone..', label='..(row.labelText or '')..', postype='..(row.posType or ''))
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
-- debugmsg('n-line for '..row.code..':'..tostring(row.gridx+1)..', '..tostring(row.gridy-1)..', '..tostring(row.gridx+row.dx+offsetx)..', '..tostring(row.gridy+row.dy+offsety)..', with width '..tostring(shape.textEW)..'px '..(shape.textES or 'no style')..' and color '..shape.textEC)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap,shape)
-- debugmsg('mark-line line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..(row.mlStyle or 'no style')..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
if color[2] then
propertyTable['stroke-opacity'] = tonumber(color[2] or '20')/100
end
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] and args['map-data-light'] ~= '' then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.4 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
comma=', '
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
local function getQset (qValue,pValue,argNumber,args)
local result={}
local sortType=string.lower(args['geo-list-sort'] or 'a')
local testValue='0'
if sortType == 'alpha' or sortType == 'a' or sortType == 'ah' or sortType == 'al' then
testValue = mw.wikibase.getLabel(qValue)
elseif sortType == 'value' or sortType == 'v' or sortType == 'vh' or sortType == 'vl'then
if qValue==nil or pValue == nil then debugmsg(mw.addWarning((qValue or 'nil')..' Qnumber for '..(pValue or 'nil')))
else
if mw.wikibase.getBestStatements(qValue,pValue)[1] then
testValue = mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.amount or '0'
if pValue == 'P2046' then
testValue = convertToSqKm(testValue, mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.unit)
end
end
end
elseif sortType == 'north' or sortType == 'n' or sortType == 'nh' or sortType == 'nl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.latitude or 0
end
elseif sortType == 'west' or sortType == 'w' or sortType == 'wh' or sortType == 'wl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.longitude or 0
end
end
result[1]=testValue
result[2]=argNumber
return result
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb floatleft">')
else table.insert(result,'<div class="thumb floatright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
--add any boundary Qvalues to map-data-light if requested
if args['geo-region'] and (args['geo-boundaries'] == '1') then
local qmaps=args['map-data-light'] or ''
if qmaps ~='' and string.sub(qmaps,#qmaps) ~= ',' then qmaps=qmaps..',' end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
if mw.wikibase.entityExists(qtemp) then
qmaps=qmaps..qtemp..',' -- see if there is a geocode boundary Qvalue to add
else debugmsg(mw.addWarning('unable to add geocode value for '..mw.wikibase.getLabel..','..qtemp))
end
end
ct=ct+1
end
end
if qmaps and qmaps ~= '' then -- add back the created/expanded list of boundary Qvalues, and remove trailing comma
args['map-data-light'] = string.sub(qmaps,1,#qmaps-1)
end
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
local qset={}
for indx,val in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') or string.match(indx,'geo%-data[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
if string.match(indx,'geo%-data[%d]+') then
local argnum=string.sub(indx,9,#indx) or ''
if args['mark-title'..argnum] == 'none' or (string.find(args['mark-title'..argnum] or 'x','nolist')== 1) then
-- debugmsg('sort values ignored '..argnum..', '..args['mark-title'..argnum])
else
qset[val]=getQset (val, (args['geo-data-type'] or 'P1082'), argnum, args)
end
end
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
local qlist=args['geo-data-list']
if qlist then
if string.sub(qlist,#qlist) ~= ',' then qlist=qlist..',' end
else
qlist=''
end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if (args['find-geo-dots'] or '1') ~= '0' and geoRegion and mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(adminRegion,adminSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
qlist=qlist..qtemp..',' -- add subregion Qvalue to the list
end
ct=ct+1
end
end
if qlist and #qlist>1 then -- create a table from the csv list
local _, c = qlist:gsub(",","")
local qtab=splitItem(qlist,c)
for xd,yd in pairs(qtab) do
yd=string.upper(yd)
if qset[yd] == nil and mw.wikibase.isValidEntityId(yd) then
dotmax=dotmax+1
qset[yd]=getQset (yd, (args['geo-data-type'] or 'P1082'), tostring(dotmax), args)
args['geo-data'..tostring(dotmax)] = yd
-- debugmsg(dotmax..' = '..(string.upper(yd) or 'Q Not found')..': '..(mw.wikibase.getLabel(yd) or 'label Not found')..': '..(mw.wikibase.getSitelink(yd) or 'link Not found'))
elseif not mw.wikibase.isValidEntityId(yd) then
debugmsg(mw.addWarning(yd..' is not a [[wikidata]] page. geo-data-list needs comma-separated Qvalues' ))
else debugmsg(mw.addWarning(yd..' is duplicate'))
end
end
end
-- qset contains set of Qvalues and can be from wikidata geo-region, list of geo-data-list Q values or individual geo-data1= etc
local ts = {} -- table to hold the keys
local useQset=false
for ky, val in pairs(qset) do
table.insert(ts, {key = ky, value = val[1], num=val[2]})
useQset=true
end
if useQset then
local order = string.lower(args['geo-list-sort'] or 'a') -- sort the list from previously stored sortkey, either Lowest or Highest first
if string.find(',al,vl,nl,wl,west,w,alpha,a,' , ','..order..',') then
table.sort(ts, function (a,b) return lessthan(a.value, b.value) end)
else
table.sort(ts, function (a,b) return morethan(a.value, b.value) end)
end
for ky,val in ipairs(ts) do -- assign values from sorted list to numbered parameter
args['numbered'..val.num] = ky -- The numbered parameter becomes the value for each dot that has a geo Qvalue, from manual, list or wiki sources.
end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) or args['geo-data'..x] then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,round(dotitem.lat,5),round(dotitem.lon,5),fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
if captionTable ~= '' and (not args['auto-caption']) then args['auto-caption']='on' end --default to include list if using geo values
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if negativeAnswer[autoOff] then captionTable='' end
if args.caption or (not negativeAnswer[autoOff]) then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then
table.insert(result,args.caption)
if captionTable ~= '' then table.insert(result,'<hr>') end
end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
if captionTable == '' then
capchk=true
else -- add the next line of content to the html table
capchk=false
end
end
end
if captionTable ~= '' then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
for k,v in pairs(captionList) do
local nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
local ngrp=v.value.group
if captionTableText[ngrp] then
-- debugmsg('table text for'..v.value.dotTag..', '..captionTableText[v.value.group][1]..', '..captionTableText[v.value.group][2]..', '..captionTableText[v.value.group][3]..' ('..captionTableText[v.value.group][4]..')')
local c1,c2
c1,c2=checkColors(shapeList[ngrp].shapeColor)
local cDot='<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;'
cDot=cDot..'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div>'
captionTable = htmlTableLine(captionTable,cDot,captionTableText[ngrp][1], captionTableText[ngrp][2], captionTableText[ngrp][3], captionTableText[ngrp][4])
end
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
-- local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<div class = "wikitable mw-collapsible ')
if string.find(autocaption,'collapsed') then table.insert(result,'mw-collapsed') end
table.insert(result,'" style="width:100%; line-height: 17px; border-style:none; text-align: center"><b>'..(args.toggletext or toggletext)..'</b>')
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if captionTable ~= '' then table.insert(result,captionTable..'</table>') end
if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
if maplist.width >= 400 and not(args['show-q-values']) then args['show-q-values'] = '1' -- default to on if wider than 400px
elseif maplist.width < 320 then args['show-q-values'] = '0' end -- turn off Qvalus if frame is too narrow, even if requested
return p._main(args)
end
return p
hjrc3ne0gokrndtkymcmms9c560jqof
204458
204453
2026-07-05T14:49:50Z
HaxelKaszëba
17492
204458
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
local geoTotalPop = 0
local captionTable = ''
local captionTableText = {}
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it may at some point be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
-- -------------- the following text variables are used within the template, and can be translated as required ---------------
-- -------------- you may wish to retain the English version as a comment, to keep track of the original phrase --------------
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext = 'Click for interactive fullscreen map with links to nearby articles'
local toggletext = 'List of numbered map items:'
local termsOfUse = 'Maps: terms of use'
local aboutOSM = 'About OpenStreetMaps'
local yards = 'yds'
local miles = 'miles'
local metres = 'm'
local km = 'km'
local sqkm = 'sq.km'
local year = 'year'
local notFound = 'not found'
local totalItems = 'Total'
local sumOfDatapoints = 'sum of datapoints'
local numberCol = 'No.' -- abbreviated for top of column of code numbers
local wikidataLink = 'Wikidata page'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.semicircle = "M0,5 C0,18.3,20,18.3,20,5 L 0,5z"
pathshape.arrowhead = "M 10,10.2 l 10,2.8 -2,-3 2,-3 -10,2.8 -0.2,0.2z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]') )
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function parseCoordFrame(coord)
if not coord then return nil, nil end
local lat, lon = string.match(coord, "{{coord|([%-%.%d]+)|([%-%.%d]+)}}")
if lat and lon then
return tonumber(lat), tonumber(lon)
end
lat, lon = string.match(coord, "([%-%.%d]+),%s*([%-%.%d]+)")
if lat and lon then
return tonumber(lat), tonumber(lon)
end
return nil, nil
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
local t= string.find(label,'wikidata') -- exclude wikidata from tooltip line
if t then
label=string.sub(label,1,t-2) -- don't show wikidata value in tooltip
if string.find(link,'wikidata') then link = '' end -- don't link on click to wikidata (if there was not another link)
end
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t, r, coord, test)
t.lat, t.lon = parseCoordFrame(coord)
if not t.lat or not t.lon then
error("maptogrid: cannot parse coord → " .. tostring(coord))
end
t.width = tonumber(t.width) or 0
t.height = tonumber(t.height) or 0
t.lonbase = tonumber(t.lonbase)
t.latbase = tonumber(t.latbase)
t.zoom = tonumber(t.zoom) or 0
local zoom = t.zoom
local x = t.width / 2 +
(
(
(math.rad(t.lon) * 6378137) -
(math.rad(t.lonbase) * 6378137)
)
/
(156543.03 * math.cos(math.rad(t.latbase)) / (2 ^ zoom))
)
* (1 - (0.075 * (math.abs(t.latbase) / 90)))
local y = t.height / 2 +
(
(
(math.log(math.tan(math.rad(t.latbase) / 2 + math.pi / 4)) * 6378137)
-
(math.log(math.tan(math.rad(t.lat) / 2 + math.pi / 4)) * 6378137)
)
/
(156543.03 * math.cos(math.rad(t.latbase)) / (2 ^ zoom))
)
* (1 - (0.075 * (math.abs(t.latbase) / 90)))
return round(x, r), round(y, r)
end
local function getScale(zoom, lat, magVal)
zoom = tonumber(zoom) or 0
lat = tonumber(lat)
if not lat then
lat = 0
end
if magVal then
magVal = tonumber(magVal)
if magVal and magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y = round(dist * 10, 1)
return tostring(y * 100) .. metres,
tostring(round(y * 109, 0)) .. yards
elseif dist < 18 then
y = round(dist, 0)
return tostring(y) .. km,
tostring(round(y * 0.621371, 1)) .. miles
elseif dist < 500 then
y = round(dist / 10)
return tostring(y * 10) .. km,
tostring(round(y * 6.21371, 0)) .. miles
else
y = round(dist / 100)
return tostring(y * 100) .. km,
tostring(round(y * 62.1371, 0)) .. miles
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function htmlTableLine (htmlTable,h1,h2,h3,h4,h5,type)
if type == 'header' then
if (h4 or '') ~= 'no-year' then h4='<th class="unsortable">'..h4 or ''..'</th>' else h4 = '' end
if (h5 or '') ~= '' then h5='<th class="unsortable">'..h5..'</th>' else h5 = '' end
return htmlTable..'<tr><th>'..(h1 or '')..'</th><th>'..(h2 or '')..'</th><th>'..(h3 or '')..'</th>'..h4..h5..'</tr>'
else
if (h4 or '') ~= 'no-year' then h4='<td>'..h4 or ''..'</td>' else h4 = '' end
if (h5 or '') ~= '' then h5='<td>'..h5..'</td>' else h5 = '' end
return htmlTable..'<tr><td>'..(h1 or '')..'</td><td>'..(h2 or '')..'</td><td>'..(h3 or '')..'</td>'..h4..h5..'</tr>'
end
end
local function formatInt(number)
if number == 0 then return notFound end
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
int = int:reverse():gsub("(%d%d%d)", "%1,")
return minus .. int:reverse():gsub("^,", "") .. fraction
end
local function convertToSqKm (value,punit)
if string.find(punit, 'Q35852') then value = value / 100 -- convert to sq km if it's hectares
elseif string.find(punit, 'Q232291') then value = value /(1/2.59) -- or this if it's square miles
elseif string.find(punit, 'Q81292') then value = value / 247.1 -- or this if it's acres
end
return value
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
local qvalue=''
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
-- Assign autovalues if a wikidata Qvalue has been supplied
-- some preliminary documentation
-- geo-data-type= default is P1082 (population)
-- other options might include number of households (P1538) and per capita income (P10622) or land area (P2046)
-- geo-data-list= this is a comma-separated list of Q values, for a manually generated list that doesn't require nymbered parameters
-- geo-scalefactor = can enlarge or shrink all the dots in proportion, depending an what the map needs
-- geo-region = needs to be a Qvalue of the larger administrative region that the settlements/sub-regions are in
-- geo-sub-region needs to be a Pvalue, but will default to P150 (some pages use 'has parts' = P527)
-- geo-data3= etc numbered Qvalue item, if used in conjunction with other shape/label parameters
-- shapeD= sets default shape, which migh be n-circle to include numbers.
-- geo-number-size= will give a fixed size for shape numbers. (defaults to 12 when using proportional dots)
-- geo-boundaries= if set to 1 will add any Qvalues for the sub-regions to 'map-data-light'.
-- show-geo-dots= to use proportional dots. Will be fixed size if 0. (set mark-sizeD=0 to have no dot at all, eg to show labels or boundaries)
-- find-geo-dots=1 or 0 (needs to also include geo-region)
-- label-posD = gets additional options: [off] gives no label, [on] will position lower centre,
-- [on+] includes numeric values beside/below label, [on^] will always put it below. [on-] always puts values beside
if args['geo-data'..argval] then -- and (args['mark-title'..argval] or '') ~= 'none' then
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
if not args['geo-number-size'] then args['geo-number-size']=12 end
if not args['label-posD'] then args['label-posD'] = 'on' end
qvalue=args['geo-data'..argval]
-- total poulation is a Qvalue wikidata page entry, or add up all the geo-data values or use a random 10000
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local thisPop=0
local areatxt=''
local yearVal=''
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
thisPop=tonumber(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.amount) or 0
if pvalue == 'P2046' then -- area, so convert from ha to sq km if needed (/100)
thisPop = convertToSqKm(thisPop, mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit)
end
if mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers and mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585 then
yearVal = mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal then yearVal = yearVal:sub(2,5) end -- wikidata reports an entire date and time, so extract the year
end
else
debugmsg(mw.addWarning(mw.wikibase.getLabel(qvalue)..' ('..qvalue..') has no recorded '..plabel))
end
local scalefactor=args['geo-scalefactor'] or 1
if argval=='1' and args['mark-size1'] and not args['mark-sizeD'] then args['mark-sizeD'] = args['mark-size1'] end
if argval=='1' and args['label-pos1'] and not args['label-posD'] then args['label-posD'] = args['label-pos1'] end
if not args['mark-size'..argval] then
if args['show-geo-dots'] == '0' then
args['mark-size'..argval] = args['mark-sizeD'] or '1' -- just use a 'point' for the label to attach to
else
if thisPop>0 then
args['mark-size'..argval] = round(math.sqrt(thisPop/geoTotalPop*100) * 8 * scalefactor,0)
else
args['mark-size'..argval] = args['mark-sizeD'] or '1'
args['shape-outline'..argval]=args['shape-outline'..argval] or args['shape-outlineH']
end
end
end
local posTemp=string.lower(multiCheck(args,'label-pos',argval,'on'))
args['label-pos'..argval] = posTemp
posTemp=splitItem(posTemp,2)[1]
if pvalue == 'P2046' then
thisPop = round(thisPop,2)
areatxt=sqkm..areatxt -- round decimals if sq km area
elseif pvalue == 'P10622' then
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
local punit=(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit or ''):match("Q.*") -- extract Q value
if punit == 'Q4917' then areatxt=' US$'
elseif punit == 'Q4916' then areatxt=' euro'
elseif punit == 'Q25224' then areatxt=' £GBP'
else areatxt=mw.wikibase.getLabel(punit)..areatxt
end
end
end
if posTemp=='off' then
else
if not args['label'..argval] then args['label'..argval] = mw.wikibase.getLabel(qvalue) end
local tline=posTemp:find("[%+%^%-]")
if tline then
local tval=posTemp:sub(tline,tline)
local eline
posTemp=posTemp:sub(1,tline-1)
if posTemp == 'left' or posTemp == 'right' or string.find(posTemp,'east') or string.find(posTemp,'west') then
if tval == '^' then eline = '^' else eline = ' ' end -- only add line break if 'forced', used for left/right
elseif tval == '+' or tval == '^' then eline = '^' -- add linebreak unless 'forced' not to
else eline = ' ' -- must be a dash, so no line break
end
args['label'..argval] = args['label'..argval]..eline..'('..formatInt(thisPop)..')'
args['label-pos'..argval] = posTemp..args['label-pos'..argval]:sub(tline+1,#args['label-pos'..argval])
end
end
if posTemp=='on' then
if argval=='1' and (not args.ldyD) then args.ldyD = args['ldy'..argval] or '0' end
if not args['ldy'..argval] then args['ldy'..argval] = args.ldyD end -- ensure ldy for that dot is added to the position
args['label-pos'..argval]='center'
local _,br = string.gsub(args['label'..argval],'%^','')
local item=splitItem(args['label-size'..argval],2)
args['ldy'..argval] = (args['ldy'..argval] or 0) + ((item[1] or 11) * ((br+1)*0.6)) + ((args['geo-number-size'] or 12) / 2)
end
local nt=''
if args['mark-title'..argval] == 'nolist' then nt='nolist,' end
if not args['mark-title'..argval] or args['mark-title'..argval] == '' or args['mark-title'..argval] == 'nolist' then
if mw.wikibase.getSitelink(qvalue) then
args['mark-title'..argval] = nt..'[['..mw.wikibase.getSitelink(qvalue)..']]'
else
args['mark-title'..argval] = nt..mw.wikibase.getLabel(qvalue)
end
end -- If mark-title is not set locally then attempt Sitelink value, or use label
if thisPop == 0 then areatxt='' end
captionTableText[argval]={}
captionTableText[argval][1]= args['mark-title'..argval]
captionTableText[argval][2]= formatInt(thisPop)
if maplist.width >= 280 then captionTableText[argval][3]= yearVal else captionTableText[argval][3]='no-year' end
if args['show-q-values'] == '1' then
captionTableText[argval][4]='<small>[[wikidata:'..qvalue..'|'..qvalue..']]</small>'
else
local nbsp = ' '
if maplist.width < 230 then nbsp = ' ' end
captionTableText[argval][4]=''
if captionTableText[argval][3] == 'no-year' then
captionTableText[argval][2] = captionTableText[argval][2]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
else
captionTableText[argval][3] = captionTableText[argval][3]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
end
end
if args['mark-title'..argval] == 'none' then
-- no title
else
args['mark-title'..argval] = args['mark-title'..argval]..' ('..plabel..' '..formatInt(thisPop)..areatxt..')'
end
if not args['mark-description'..argval] then args['mark-description'..argval] = (mw.wikibase.getDescription(qvalue) or '')..'<br>' end
if (not args['mark-image'..argval]) and (mw.wikibase.getBestStatements(qvalue,'P18')[1]) then
args['mark-image'..argval] = mw.wikibase.getBestStatements(qvalue,'P18')[1].mainsnak.datavalue.value
end
item = multiCheck(args,'shape',argval,'circle') --default to circle if geo-data
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
else
item = multiCheck(args,'shape',argval,'image')
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
end
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,(shapeResult[nval].shapeSize or '14px')) -- add geo-data as fallback if already assigned
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
if args['shape-angle'..argval] then shapeResult[nval].shapeAngleArg=true else shapeResult[nval].shapeAngleArg=false end
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
shapeResult[nval].textAT=args['label-attribute'..argval] or ''
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
if args['label-angle'..argval] then shapeResult[nval].textNGarg=true else shapeResult[nval].textNGarg=false end
--sgd=dotTag attributes
shapeResult[nval].tagSize=(args['geo-number-size'] or tostring(shapeHeight*1.5))..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
if itemTab[1] == nil or itemTab[2] == nil then
debugmsg(mw.addWarning('Unable to read coordinates for mark-coord'..argval))
else
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
end
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
if string.find(item,'nolist') then dotItem.param1='nolist,' item = string.sub(args['mark-title'..argval],8) or args['label'..argval] end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
-- debugmsg('dlink for '..item..' is '..dotItem.dlink)
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
-- debugmsg(dotItem.code..' = '..dotItem.lat..','..dotItem.lon..': '..dotItem.labelText..' - '..dotItem.title..', '..shapeResult[nval].shapeSize)
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['geo-data'..x] and (not args['mark-coord'..x]) and (not args['mark-lat'..x]) then
-- get lat and lon values from wikidata and put them into the args.lat and args.lon for use wherever needed
if (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1]) then
args['mark-lat'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.latitude) or '0'
args['mark-lon'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.longitude) or '0'
local p=args['geo-data-type'] or 'P1082'
if mw.wikibase.getBestStatements(args['geo-data'..x],p)[1] then
local n = tonumber(mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.amount) or 0
if p == 'P2046' then -- area, so convert to sq km if needed (/100)
n = convertToSqKm( n, mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.unit)
end
geoTotalPop = geoTotalPop + n
end
else debugmsg(mw.addWarning('could not find coords for '..mw.wikibase.getLabel(args['geo-data'..x])..', '..args['geo-data'..x]))
end
end
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form (needs a second table, seemingly)
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
local unitConvert=1
local areaUnits=''
if args['geo-region'] and (args['find-geo-dots'] or '1') ~= '0' then
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
local subhead='' -- Assemble the subhead content, a line of text about the region as a whole
if region and mw.wikibase.getBestStatements(region,pvalue)[1] then
local punit=mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.unit:match("Q.*") -- extract Q value
if pvalue == 'P10622' then -- add other currency-based pvalues here
areaUnits=mw.wikibase.getLabel(punit)
end
if punit == 'Q35852' then unitConvert=100 areaUnits = ' '..sqkm -- if its hectares, convert to sq km
elseif punit == 'Q232291' then unitConvert = (1/2.59) areaUnits = ' '..sqkm -- or this if it's square miles
elseif punit == 'Q81292' then unitConvert = 247.1 areaUnits = ' '..sqkm -- or this if it's acres
elseif punit == 'Q712226' then unitConvert = 1 areaUnits = ' '..sqkm -- or this if it's already sq km
elseif punit == 'Q4917' then areaUnits=' US$' -- replace long version with some shortform currencies ()
elseif punit == 'Q4916' then areaUnits=' euro'
elseif punit == 'Q25224' then areaUnits=' £GBP'
end
-- if pvalue == 'P2046' then
-- may need to add other instances of sq km so add in other P-items here if they need units showing
-- end
geoTotalPop=(mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.amount) / unitConvert
local yearVal
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers then
yearVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal and yearVal:sub(1,1) == '+' then yearVal = yearVal:sub(2,5)
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459 and mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1] then
local methodVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1].datavalue.value.id
if methodVal then yearVal=yearVal..' '..mw.wikibase.getLabel(methodVal) end
end
yearVal = ' ('..yearVal..')'
else yearVal =''
end
end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ([[wikidata:'..region..']]) = '..formatInt(round(geoTotalPop),1)..areaUnits..yearVal
else
if pvalue == 'P2046' then areaUnits = sqkm end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ('..sumOfDatapoints..') = '..formatInt(round(geoTotalPop),1)..areaUnits
end
if maplist.width < 350 and #plabel > 4 then plabel=string.sub((plabel or ' '),1,3)..'.' end
if maplist.width < 330 then numberCol = ' ' end
if maplist.width < 280 then year = 'no-year' end
captionTable = subhead
if (args['show-q-values'] or '') ~= '1' then
captionTable = captionTable..'<div style="float:right;"><small> (WD = '..wikidataLink..')</small></div><br>'
wikidataLink=''
end
captionTable = captionTable..'<table class="wikitable plainrowheaders sortable mw-collapsible mw-collapsed" style="width: 100%;"><caption>'..(args.toggletext or toggletext)..' </caption>'
captionTable = htmlTableLine(captionTable,numberCol,mw.wikibase.getLabel(region),plabel..' '..areaUnits, year, wikidataLink,'header')
end
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double,shape)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
local tw
if double <= 1 then
tw = tonumber(string.match(oWid,"[%.%d]+"))/2 -- shift centre by half the line width
else
tw = tonumber(string.match(oWid,"[%.%d]+")) + (double/2) - 0.5 -- shift centre by a line width + half the gap
end
-- debugmsg(oWid..'+'..double..'/2='..tw)
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2) ).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) - tw).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
local lineAngle=math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi
-- if x1 == x2 then lineAngle=90 end
table.insert(result,"transform: rotate("..lineAngle.."deg);\"></div>")
-- debugmsg('text angle started as '..shape.textNG..' and lineAngle = '..lineAngle)
if (lineAngle < -90) or (lineAngle >90) then lineAngle=lineAngle-180 end
if shape and not shape.textNGarg then shape.textNG=tostring(round(lineAngle,0))..'deg' end -- Put in line angle for a default text angle as well
-- if (y2 >= y1) and (x2 <= x1) then lineAngle=lineAngle-180 debugmsg('arrow angle corrected') end
if x2 < x1 then lineAngle=lineAngle-180 end
if shape and not shape.shapeAngleArg and shape.shape == 'arrowhead' then shape.shapeAngle=tostring(round(lineAngle,0))..'deg' end -- line angle for arrowhead
-- debugmsg('text angle is now '..shape.textNG..'with arrow at '..lineAngle)
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if string.find(shape.shape, 'svgpath,') and #shape.shape > 9 then
pathshape.svgpath=string.sub(shape.shape,9)
shape.shape='svgpath'
end
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if shape.shape == 'arrowhead' then -- enlarge arrowheads, as they only go to the middle of the notional shape-size
w=w*2.5
h=h*2.5
end
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result," scale("..tostring(w/16)..", "..tostring(h/16)..")")
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy+2 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,rndg=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = (w/2)+1,(h/2)+1
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
-- debugmsg('r-(w/2) > widthzone r-w/2='..tostring(r-(w/2))..', widthzone='..widthzone..', label='..(row.labelText or '')..', postype='..(row.posType or ''))
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
-- debugmsg('n-line for '..row.code..':'..tostring(row.gridx+1)..', '..tostring(row.gridy-1)..', '..tostring(row.gridx+row.dx+offsetx)..', '..tostring(row.gridy+row.dy+offsety)..', with width '..tostring(shape.textEW)..'px '..(shape.textES or 'no style')..' and color '..shape.textEC)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap,shape)
-- debugmsg('mark-line line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..(row.mlStyle or 'no style')..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
if color[2] then
propertyTable['stroke-opacity'] = tonumber(color[2] or '20')/100
end
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] and args['map-data-light'] ~= '' then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.4 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
comma=', '
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
local function getQset (qValue,pValue,argNumber,args)
local result={}
local sortType=string.lower(args['geo-list-sort'] or 'a')
local testValue='0'
if sortType == 'alpha' or sortType == 'a' or sortType == 'ah' or sortType == 'al' then
testValue = mw.wikibase.getLabel(qValue)
elseif sortType == 'value' or sortType == 'v' or sortType == 'vh' or sortType == 'vl'then
if qValue==nil or pValue == nil then debugmsg(mw.addWarning((qValue or 'nil')..' Qnumber for '..(pValue or 'nil')))
else
if mw.wikibase.getBestStatements(qValue,pValue)[1] then
testValue = mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.amount or '0'
if pValue == 'P2046' then
testValue = convertToSqKm(testValue, mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.unit)
end
end
end
elseif sortType == 'north' or sortType == 'n' or sortType == 'nh' or sortType == 'nl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.latitude or 0
end
elseif sortType == 'west' or sortType == 'w' or sortType == 'wh' or sortType == 'wl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.longitude or 0
end
end
result[1]=testValue
result[2]=argNumber
return result
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb floatleft">')
else table.insert(result,'<div class="thumb floatright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
--add any boundary Qvalues to map-data-light if requested
if args['geo-region'] and (args['geo-boundaries'] == '1') then
local qmaps=args['map-data-light'] or ''
if qmaps ~='' and string.sub(qmaps,#qmaps) ~= ',' then qmaps=qmaps..',' end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
if mw.wikibase.entityExists(qtemp) then
qmaps=qmaps..qtemp..',' -- see if there is a geocode boundary Qvalue to add
else debugmsg(mw.addWarning('unable to add geocode value for '..mw.wikibase.getLabel..','..qtemp))
end
end
ct=ct+1
end
end
if qmaps and qmaps ~= '' then -- add back the created/expanded list of boundary Qvalues, and remove trailing comma
args['map-data-light'] = string.sub(qmaps,1,#qmaps-1)
end
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
local qset={}
for indx,val in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') or string.match(indx,'geo%-data[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
if string.match(indx,'geo%-data[%d]+') then
local argnum=string.sub(indx,9,#indx) or ''
if args['mark-title'..argnum] == 'none' or (string.find(args['mark-title'..argnum] or 'x','nolist')== 1) then
-- debugmsg('sort values ignored '..argnum..', '..args['mark-title'..argnum])
else
qset[val]=getQset (val, (args['geo-data-type'] or 'P1082'), argnum, args)
end
end
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
local qlist=args['geo-data-list']
if qlist then
if string.sub(qlist,#qlist) ~= ',' then qlist=qlist..',' end
else
qlist=''
end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if (args['find-geo-dots'] or '1') ~= '0' and geoRegion and mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(adminRegion,adminSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
qlist=qlist..qtemp..',' -- add subregion Qvalue to the list
end
ct=ct+1
end
end
if qlist and #qlist>1 then -- create a table from the csv list
local _, c = qlist:gsub(",","")
local qtab=splitItem(qlist,c)
for xd,yd in pairs(qtab) do
yd=string.upper(yd)
if qset[yd] == nil and mw.wikibase.isValidEntityId(yd) then
dotmax=dotmax+1
qset[yd]=getQset (yd, (args['geo-data-type'] or 'P1082'), tostring(dotmax), args)
args['geo-data'..tostring(dotmax)] = yd
-- debugmsg(dotmax..' = '..(string.upper(yd) or 'Q Not found')..': '..(mw.wikibase.getLabel(yd) or 'label Not found')..': '..(mw.wikibase.getSitelink(yd) or 'link Not found'))
elseif not mw.wikibase.isValidEntityId(yd) then
debugmsg(mw.addWarning(yd..' is not a [[wikidata]] page. geo-data-list needs comma-separated Qvalues' ))
else debugmsg(mw.addWarning(yd..' is duplicate'))
end
end
end
-- qset contains set of Qvalues and can be from wikidata geo-region, list of geo-data-list Q values or individual geo-data1= etc
local ts = {} -- table to hold the keys
local useQset=false
for ky, val in pairs(qset) do
table.insert(ts, {key = ky, value = val[1], num=val[2]})
useQset=true
end
if useQset then
local order = string.lower(args['geo-list-sort'] or 'a') -- sort the list from previously stored sortkey, either Lowest or Highest first
if string.find(',al,vl,nl,wl,west,w,alpha,a,' , ','..order..',') then
table.sort(ts, function (a,b) return lessthan(a.value, b.value) end)
else
table.sort(ts, function (a,b) return morethan(a.value, b.value) end)
end
for ky,val in ipairs(ts) do -- assign values from sorted list to numbered parameter
args['numbered'..val.num] = ky -- The numbered parameter becomes the value for each dot that has a geo Qvalue, from manual, list or wiki sources.
end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) or args['geo-data'..x] then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,round(dotitem.lat,5),round(dotitem.lon,5),fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
if captionTable ~= '' and (not args['auto-caption']) then args['auto-caption']='on' end --default to include list if using geo values
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if negativeAnswer[autoOff] then captionTable='' end
if args.caption or (not negativeAnswer[autoOff]) then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then
table.insert(result,args.caption)
if captionTable ~= '' then table.insert(result,'<hr>') end
end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
if captionTable == '' then
capchk=true
else -- add the next line of content to the html table
capchk=false
end
end
end
if captionTable ~= '' then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
for k,v in pairs(captionList) do
local nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
local ngrp=v.value.group
if captionTableText[ngrp] then
-- debugmsg('table text for'..v.value.dotTag..', '..captionTableText[v.value.group][1]..', '..captionTableText[v.value.group][2]..', '..captionTableText[v.value.group][3]..' ('..captionTableText[v.value.group][4]..')')
local c1,c2
c1,c2=checkColors(shapeList[ngrp].shapeColor)
local cDot='<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;'
cDot=cDot..'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div>'
captionTable = htmlTableLine(captionTable,cDot,captionTableText[ngrp][1], captionTableText[ngrp][2], captionTableText[ngrp][3], captionTableText[ngrp][4])
end
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
-- local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<div class = "wikitable mw-collapsible ')
if string.find(autocaption,'collapsed') then table.insert(result,'mw-collapsed') end
table.insert(result,'" style="width:100%; line-height: 17px; border-style:none; text-align: center"><b>'..(args.toggletext or toggletext)..'</b>')
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if captionTable ~= '' then table.insert(result,captionTable..'</table>') end
if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
if maplist.width >= 400 and not(args['show-q-values']) then args['show-q-values'] = '1' -- default to on if wider than 400px
elseif maplist.width < 320 then args['show-q-values'] = '0' end -- turn off Qvalus if frame is too narrow, even if requested
return p._main(args)
end
return p
n1ouydkukif136v1lyxxtbx7a56z1oc
204459
204458
2026-07-05T14:58:52Z
HaxelKaszëba
17492
204459
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
local geoTotalPop = 0
local captionTable = ''
local captionTableText = {}
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it may at some point be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
-- -------------- the following text variables are used within the template, and can be translated as required ---------------
-- -------------- you may wish to retain the English version as a comment, to keep track of the original phrase --------------
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext = 'Click for interactive fullscreen map with links to nearby articles'
local toggletext = 'List of numbered map items:'
local termsOfUse = 'Maps: terms of use'
local aboutOSM = 'About OpenStreetMaps'
local yards = 'yds'
local miles = 'miles'
local metres = 'm'
local km = 'km'
local sqkm = 'sq.km'
local year = 'year'
local notFound = 'not found'
local totalItems = 'Total'
local sumOfDatapoints = 'sum of datapoints'
local numberCol = 'No.' -- abbreviated for top of column of code numbers
local wikidataLink = 'Wikidata page'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.semicircle = "M0,5 C0,18.3,20,18.3,20,5 L 0,5z"
pathshape.arrowhead = "M 10,10.2 l 10,2.8 -2,-3 2,-3 -10,2.8 -0.2,0.2z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]') )
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) and tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%-?%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%-?%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
local t= string.find(label,'wikidata') -- exclude wikidata from tooltip line
if t then
label=string.sub(label,1,t-2) -- don't show wikidata value in tooltip
if string.find(link,'wikidata') then link = '' end -- don't link on click to wikidata (if there was not another link)
end
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r,test)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x,y
x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.075*(math.abs(t.latbase)/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat,magVal)
if magVal and magVal>1 and magVal <=2 then zoom=zoom+(magVal-1) end
local dist=(156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom))/17
local y
if dist < 1 then
y=(round(dist*10,1))
return tostring(y*100)..metres, tostring(round(y*109,0))..yards
elseif dist <18 then
y=(round(dist,0))
return tostring(y)..km, tostring(round(y*0.621371,1))..miles
elseif dist <500 then
y=round(dist/10)
return tostring(y*10)..km, tostring(round(y*6.21371,0))..miles
else
y=round(dist/100)
return tostring(y*100)..km, tostring(round(y*62.1371,0))..miles
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function htmlTableLine (htmlTable,h1,h2,h3,h4,h5,type)
if type == 'header' then
if (h4 or '') ~= 'no-year' then h4='<th class="unsortable">'..h4 or ''..'</th>' else h4 = '' end
if (h5 or '') ~= '' then h5='<th class="unsortable">'..h5..'</th>' else h5 = '' end
return htmlTable..'<tr><th>'..(h1 or '')..'</th><th>'..(h2 or '')..'</th><th>'..(h3 or '')..'</th>'..h4..h5..'</tr>'
else
if (h4 or '') ~= 'no-year' then h4='<td>'..h4 or ''..'</td>' else h4 = '' end
if (h5 or '') ~= '' then h5='<td>'..h5..'</td>' else h5 = '' end
return htmlTable..'<tr><td>'..(h1 or '')..'</td><td>'..(h2 or '')..'</td><td>'..(h3 or '')..'</td>'..h4..h5..'</tr>'
end
end
local function formatInt(number)
if number == 0 then return notFound end
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
int = int:reverse():gsub("(%d%d%d)", "%1,")
return minus .. int:reverse():gsub("^,", "") .. fraction
end
local function convertToSqKm (value,punit)
if string.find(punit, 'Q35852') then value = value / 100 -- convert to sq km if it's hectares
elseif string.find(punit, 'Q232291') then value = value /(1/2.59) -- or this if it's square miles
elseif string.find(punit, 'Q81292') then value = value / 247.1 -- or this if it's acres
end
return value
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
local qvalue=''
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
-- Assign autovalues if a wikidata Qvalue has been supplied
-- some preliminary documentation
-- geo-data-type= default is P1082 (population)
-- other options might include number of households (P1538) and per capita income (P10622) or land area (P2046)
-- geo-data-list= this is a comma-separated list of Q values, for a manually generated list that doesn't require nymbered parameters
-- geo-scalefactor = can enlarge or shrink all the dots in proportion, depending an what the map needs
-- geo-region = needs to be a Qvalue of the larger administrative region that the settlements/sub-regions are in
-- geo-sub-region needs to be a Pvalue, but will default to P150 (some pages use 'has parts' = P527)
-- geo-data3= etc numbered Qvalue item, if used in conjunction with other shape/label parameters
-- shapeD= sets default shape, which migh be n-circle to include numbers.
-- geo-number-size= will give a fixed size for shape numbers. (defaults to 12 when using proportional dots)
-- geo-boundaries= if set to 1 will add any Qvalues for the sub-regions to 'map-data-light'.
-- show-geo-dots= to use proportional dots. Will be fixed size if 0. (set mark-sizeD=0 to have no dot at all, eg to show labels or boundaries)
-- find-geo-dots=1 or 0 (needs to also include geo-region)
-- label-posD = gets additional options: [off] gives no label, [on] will position lower centre,
-- [on+] includes numeric values beside/below label, [on^] will always put it below. [on-] always puts values beside
if args['geo-data'..argval] then -- and (args['mark-title'..argval] or '') ~= 'none' then
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
if not args['geo-number-size'] then args['geo-number-size']=12 end
if not args['label-posD'] then args['label-posD'] = 'on' end
qvalue=args['geo-data'..argval]
-- total poulation is a Qvalue wikidata page entry, or add up all the geo-data values or use a random 10000
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local thisPop=0
local areatxt=''
local yearVal=''
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
thisPop=tonumber(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.amount) or 0
if pvalue == 'P2046' then -- area, so convert from ha to sq km if needed (/100)
thisPop = convertToSqKm(thisPop, mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit)
end
if mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers and mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585 then
yearVal = mw.wikibase.getBestStatements(qvalue,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal then yearVal = yearVal:sub(2,5) end -- wikidata reports an entire date and time, so extract the year
end
else
debugmsg(mw.addWarning(mw.wikibase.getLabel(qvalue)..' ('..qvalue..') has no recorded '..plabel))
end
local scalefactor=args['geo-scalefactor'] or 1
if argval=='1' and args['mark-size1'] and not args['mark-sizeD'] then args['mark-sizeD'] = args['mark-size1'] end
if argval=='1' and args['label-pos1'] and not args['label-posD'] then args['label-posD'] = args['label-pos1'] end
if not args['mark-size'..argval] then
if args['show-geo-dots'] == '0' then
args['mark-size'..argval] = args['mark-sizeD'] or '1' -- just use a 'point' for the label to attach to
else
if thisPop>0 then
args['mark-size'..argval] = round(math.sqrt(thisPop/geoTotalPop*100) * 8 * scalefactor,0)
else
args['mark-size'..argval] = args['mark-sizeD'] or '1'
args['shape-outline'..argval]=args['shape-outline'..argval] or args['shape-outlineH']
end
end
end
local posTemp=string.lower(multiCheck(args,'label-pos',argval,'on'))
args['label-pos'..argval] = posTemp
posTemp=splitItem(posTemp,2)[1]
if pvalue == 'P2046' then
thisPop = round(thisPop,2)
areatxt=sqkm..areatxt -- round decimals if sq km area
elseif pvalue == 'P10622' then
if mw.wikibase.getBestStatements(qvalue,pvalue)[1] then
local punit=(mw.wikibase.getBestStatements(qvalue,pvalue)[1].mainsnak.datavalue.value.unit or ''):match("Q.*") -- extract Q value
if punit == 'Q4917' then areatxt=' US$'
elseif punit == 'Q4916' then areatxt=' euro'
elseif punit == 'Q25224' then areatxt=' £GBP'
else areatxt=mw.wikibase.getLabel(punit)..areatxt
end
end
end
if posTemp=='off' then
else
if not args['label'..argval] then args['label'..argval] = mw.wikibase.getLabel(qvalue) end
local tline=posTemp:find("[%+%^%-]")
if tline then
local tval=posTemp:sub(tline,tline)
local eline
posTemp=posTemp:sub(1,tline-1)
if posTemp == 'left' or posTemp == 'right' or string.find(posTemp,'east') or string.find(posTemp,'west') then
if tval == '^' then eline = '^' else eline = ' ' end -- only add line break if 'forced', used for left/right
elseif tval == '+' or tval == '^' then eline = '^' -- add linebreak unless 'forced' not to
else eline = ' ' -- must be a dash, so no line break
end
args['label'..argval] = args['label'..argval]..eline..'('..formatInt(thisPop)..')'
args['label-pos'..argval] = posTemp..args['label-pos'..argval]:sub(tline+1,#args['label-pos'..argval])
end
end
if posTemp=='on' then
if argval=='1' and (not args.ldyD) then args.ldyD = args['ldy'..argval] or '0' end
if not args['ldy'..argval] then args['ldy'..argval] = args.ldyD end -- ensure ldy for that dot is added to the position
args['label-pos'..argval]='center'
local _,br = string.gsub(args['label'..argval],'%^','')
local item=splitItem(args['label-size'..argval],2)
args['ldy'..argval] = (args['ldy'..argval] or 0) + ((item[1] or 11) * ((br+1)*0.6)) + ((args['geo-number-size'] or 12) / 2)
end
local nt=''
if args['mark-title'..argval] == 'nolist' then nt='nolist,' end
if not args['mark-title'..argval] or args['mark-title'..argval] == '' or args['mark-title'..argval] == 'nolist' then
if mw.wikibase.getSitelink(qvalue) then
args['mark-title'..argval] = nt..'[['..mw.wikibase.getSitelink(qvalue)..']]'
else
args['mark-title'..argval] = nt..mw.wikibase.getLabel(qvalue)
end
end -- If mark-title is not set locally then attempt Sitelink value, or use label
if thisPop == 0 then areatxt='' end
captionTableText[argval]={}
captionTableText[argval][1]= args['mark-title'..argval]
captionTableText[argval][2]= formatInt(thisPop)
if maplist.width >= 280 then captionTableText[argval][3]= yearVal else captionTableText[argval][3]='no-year' end
if args['show-q-values'] == '1' then
captionTableText[argval][4]='<small>[[wikidata:'..qvalue..'|'..qvalue..']]</small>'
else
local nbsp = ' '
if maplist.width < 230 then nbsp = ' ' end
captionTableText[argval][4]=''
if captionTableText[argval][3] == 'no-year' then
captionTableText[argval][2] = captionTableText[argval][2]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
else
captionTableText[argval][3] = captionTableText[argval][3]..'<small><sup>'..nbsp..'[[wikidata:'..qvalue..'|WD]]</sup></small>'
end
end
if args['mark-title'..argval] == 'none' then
-- no title
else
args['mark-title'..argval] = args['mark-title'..argval]..' ('..plabel..' '..formatInt(thisPop)..areatxt..')'
end
if not args['mark-description'..argval] then args['mark-description'..argval] = (mw.wikibase.getDescription(qvalue) or '')..'<br>' end
if (not args['mark-image'..argval]) and (mw.wikibase.getBestStatements(qvalue,'P18')[1]) then
args['mark-image'..argval] = mw.wikibase.getBestStatements(qvalue,'P18')[1].mainsnak.datavalue.value
end
item = multiCheck(args,'shape',argval,'circle') --default to circle if geo-data
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
else
item = multiCheck(args,'shape',argval,'image')
if not string.find(item,'svgpath,') then item=string.lower(item) end -- ensure svgpath items retain original case values
end
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,(shapeResult[nval].shapeSize or '14px')) -- add geo-data as fallback if already assigned
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
if args['shape-angle'..argval] then shapeResult[nval].shapeAngleArg=true else shapeResult[nval].shapeAngleArg=false end
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
shapeResult[nval].textAT=args['label-attribute'..argval] or ''
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
if args['label-angle'..argval] then shapeResult[nval].textNGarg=true else shapeResult[nval].textNGarg=false end
--sgd=dotTag attributes
shapeResult[nval].tagSize=(args['geo-number-size'] or tostring(shapeHeight*1.5))..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
if itemTab[1] == nil or itemTab[2] == nil then
debugmsg(mw.addWarning('Unable to read coordinates for mark-coord'..argval))
else
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
end
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
if string.find(item,'nolist') then dotItem.param1='nolist,' item = string.sub(args['mark-title'..argval],8) or args['label'..argval] end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
-- debugmsg('dlink for '..item..' is '..dotItem.dlink)
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
-- debugmsg(dotItem.code..' = '..dotItem.lat..','..dotItem.lon..': '..dotItem.labelText..' - '..dotItem.title..', '..shapeResult[nval].shapeSize)
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['geo-data'..x] and (not args['mark-coord'..x]) and (not args['mark-lat'..x]) then
-- get lat and lon values from wikidata and put them into the args.lat and args.lon for use wherever needed
if (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1]) then
args['mark-lat'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.latitude) or '0'
args['mark-lon'..x] = (mw.wikibase.getBestStatements(args['geo-data'..x],'P625')[1].mainsnak.datavalue.value.longitude) or '0'
local p=args['geo-data-type'] or 'P1082'
if mw.wikibase.getBestStatements(args['geo-data'..x],p)[1] then
local n = tonumber(mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.amount) or 0
if p == 'P2046' then -- area, so convert to sq km if needed (/100)
n = convertToSqKm( n, mw.wikibase.getBestStatements(args['geo-data'..x],p)[1].mainsnak.datavalue.value.unit)
end
geoTotalPop = geoTotalPop + n
end
else debugmsg(mw.addWarning('could not find coords for '..mw.wikibase.getLabel(args['geo-data'..x])..', '..args['geo-data'..x]))
end
end
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form (needs a second table, seemingly)
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
local unitConvert=1
local areaUnits=''
if args['geo-region'] and (args['find-geo-dots'] or '1') ~= '0' then
local region = args['geo-region'] -- or mw.wikibase.getEntityIdForCurrentPage()
local pvalue = args['geo-data-type'] or 'P1082'
local plabel = mw.wikibase.getLabel(pvalue)
local subhead='' -- Assemble the subhead content, a line of text about the region as a whole
if region and mw.wikibase.getBestStatements(region,pvalue)[1] then
local punit=mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.unit:match("Q.*") -- extract Q value
if pvalue == 'P10622' then -- add other currency-based pvalues here
areaUnits=mw.wikibase.getLabel(punit)
end
if punit == 'Q35852' then unitConvert=100 areaUnits = ' '..sqkm -- if its hectares, convert to sq km
elseif punit == 'Q232291' then unitConvert = (1/2.59) areaUnits = ' '..sqkm -- or this if it's square miles
elseif punit == 'Q81292' then unitConvert = 247.1 areaUnits = ' '..sqkm -- or this if it's acres
elseif punit == 'Q712226' then unitConvert = 1 areaUnits = ' '..sqkm -- or this if it's already sq km
elseif punit == 'Q4917' then areaUnits=' US$' -- replace long version with some shortform currencies ()
elseif punit == 'Q4916' then areaUnits=' euro'
elseif punit == 'Q25224' then areaUnits=' £GBP'
end
-- if pvalue == 'P2046' then
-- may need to add other instances of sq km so add in other P-items here if they need units showing
-- end
geoTotalPop=(mw.wikibase.getBestStatements(region,pvalue)[1].mainsnak.datavalue.value.amount) / unitConvert
local yearVal
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers then
yearVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P585[1].datavalue.value.time
if yearVal and yearVal:sub(1,1) == '+' then yearVal = yearVal:sub(2,5)
if mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459 and mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1] then
local methodVal = mw.wikibase.getBestStatements(region,pvalue)[1].qualifiers.P459[1].datavalue.value.id
if methodVal then yearVal=yearVal..' '..mw.wikibase.getLabel(methodVal) end
end
yearVal = ' ('..yearVal..')'
else yearVal =''
end
end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ([[wikidata:'..region..']]) = '..formatInt(round(geoTotalPop),1)..areaUnits..yearVal
else
if pvalue == 'P2046' then areaUnits = sqkm end
subhead=totalItems..' '..mw.wikibase.getLabel(region)..' '..plabel..' ('..sumOfDatapoints..') = '..formatInt(round(geoTotalPop),1)..areaUnits
end
if maplist.width < 350 and #plabel > 4 then plabel=string.sub((plabel or ' '),1,3)..'.' end
if maplist.width < 330 then numberCol = ' ' end
if maplist.width < 280 then year = 'no-year' end
captionTable = subhead
if (args['show-q-values'] or '') ~= '1' then
captionTable = captionTable..'<div style="float:right;"><small> (WD = '..wikidataLink..')</small></div><br>'
wikidataLink=''
end
captionTable = captionTable..'<table class="wikitable plainrowheaders sortable mw-collapsible mw-collapsed" style="width: 100%;"><caption>'..(args.toggletext or toggletext)..' </caption>'
captionTable = htmlTableLine(captionTable,numberCol,mw.wikibase.getLabel(region),plabel..' '..areaUnits, year, wikidataLink,'header')
end
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double,shape)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
local tw
if double <= 1 then
tw = tonumber(string.match(oWid,"[%.%d]+"))/2 -- shift centre by half the line width
else
tw = tonumber(string.match(oWid,"[%.%d]+")) + (double/2) - 0.5 -- shift centre by a line width + half the gap
end
-- debugmsg(oWid..'+'..double..'/2='..tw)
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2) ).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) - tw).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
local lineAngle=math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi
-- if x1 == x2 then lineAngle=90 end
table.insert(result,"transform: rotate("..lineAngle.."deg);\"></div>")
-- debugmsg('text angle started as '..shape.textNG..' and lineAngle = '..lineAngle)
if (lineAngle < -90) or (lineAngle >90) then lineAngle=lineAngle-180 end
if shape and not shape.textNGarg then shape.textNG=tostring(round(lineAngle,0))..'deg' end -- Put in line angle for a default text angle as well
-- if (y2 >= y1) and (x2 <= x1) then lineAngle=lineAngle-180 debugmsg('arrow angle corrected') end
if x2 < x1 then lineAngle=lineAngle-180 end
if shape and not shape.shapeAngleArg and shape.shape == 'arrowhead' then shape.shapeAngle=tostring(round(lineAngle,0))..'deg' end -- line angle for arrowhead
-- debugmsg('text angle is now '..shape.textNG..'with arrow at '..lineAngle)
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if string.find(shape.shape, 'svgpath,') and #shape.shape > 9 then
pathshape.svgpath=string.sub(shape.shape,9)
shape.shape='svgpath'
end
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if shape.shape == 'arrowhead' then -- enlarge arrowheads, as they only go to the middle of the notional shape-size
w=w*2.5
h=h*2.5
end
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result," scale("..tostring(w/16)..", "..tostring(h/16)..")")
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy+2 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,rndg=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = (w/2)+1,(h/2)+1
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
-- debugmsg('r-(w/2) > widthzone r-w/2='..tostring(r-(w/2))..', widthzone='..widthzone..', label='..(row.labelText or '')..', postype='..(row.posType or ''))
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
-- debugmsg('n-line for '..row.code..':'..tostring(row.gridx+1)..', '..tostring(row.gridy-1)..', '..tostring(row.gridx+row.dx+offsetx)..', '..tostring(row.gridy+row.dy+offsety)..', with width '..tostring(shape.textEW)..'px '..(shape.textES or 'no style')..' and color '..shape.textEC)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap,shape)
-- debugmsg('mark-line line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..(row.mlStyle or 'no style')..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
if color[2] then
propertyTable['stroke-opacity'] = tonumber(color[2] or '20')/100
end
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] and args['map-data-light'] ~= '' then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.4 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.2
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
comma=', '
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
local function getQset (qValue,pValue,argNumber,args)
local result={}
local sortType=string.lower(args['geo-list-sort'] or 'a')
local testValue='0'
if sortType == 'alpha' or sortType == 'a' or sortType == 'ah' or sortType == 'al' then
testValue = mw.wikibase.getLabel(qValue)
elseif sortType == 'value' or sortType == 'v' or sortType == 'vh' or sortType == 'vl'then
if qValue==nil or pValue == nil then debugmsg(mw.addWarning((qValue or 'nil')..' Qnumber for '..(pValue or 'nil')))
else
if mw.wikibase.getBestStatements(qValue,pValue)[1] then
testValue = mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.amount or '0'
if pValue == 'P2046' then
testValue = convertToSqKm(testValue, mw.wikibase.getBestStatements(qValue,pValue)[1].mainsnak.datavalue.value.unit)
end
end
end
elseif sortType == 'north' or sortType == 'n' or sortType == 'nh' or sortType == 'nl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.latitude or 0
end
elseif sortType == 'west' or sortType == 'w' or sortType == 'wh' or sortType == 'wl' then
if mw.wikibase.getBestStatements(qValue,'P625')[1] then
testValue = mw.wikibase.getBestStatements(qValue,'P625')[1].mainsnak.datavalue.value.longitude or 0
end
end
result[1]=testValue
result[2]=argNumber
return result
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb floatleft">')
else table.insert(result,'<div class="thumb floatright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
--add any boundary Qvalues to map-data-light if requested
if args['geo-region'] and (args['geo-boundaries'] == '1') then
local qmaps=args['map-data-light'] or ''
if qmaps ~='' and string.sub(qmaps,#qmaps) ~= ',' then qmaps=qmaps..',' end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
if mw.wikibase.entityExists(qtemp) then
qmaps=qmaps..qtemp..',' -- see if there is a geocode boundary Qvalue to add
else debugmsg(mw.addWarning('unable to add geocode value for '..mw.wikibase.getLabel..','..qtemp))
end
end
ct=ct+1
end
end
if qmaps and qmaps ~= '' then -- add back the created/expanded list of boundary Qvalues, and remove trailing comma
args['map-data-light'] = string.sub(qmaps,1,#qmaps-1)
end
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
local qset={}
for indx,val in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') or string.match(indx,'geo%-data[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
if string.match(indx,'geo%-data[%d]+') then
local argnum=string.sub(indx,9,#indx) or ''
if args['mark-title'..argnum] == 'none' or (string.find(args['mark-title'..argnum] or 'x','nolist')== 1) then
-- debugmsg('sort values ignored '..argnum..', '..args['mark-title'..argnum])
else
qset[val]=getQset (val, (args['geo-data-type'] or 'P1082'), argnum, args)
end
end
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
local qlist=args['geo-data-list']
if qlist then
if string.sub(qlist,#qlist) ~= ',' then qlist=qlist..',' end
else
qlist=''
end
local geoRegion = args['geo-region'] -- or add base-page here
local geoSubRegion = args['geo-sub-region'] or 'P150'
if not mw.wikibase.isValidEntityId(geoSubRegion) then geoSubRegion = 'P150' end
if (args['find-geo-dots'] or '1') ~= '0' and geoRegion and mw.wikibase.isValidEntityId(geoRegion) then
local ct=1
local qtemp
while (mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct]) do
-- debugmsg('table '..ct..' - '..mw.wikibase.getBestStatements(adminRegion,adminSubRegion)[ct].mainsnak.datavalue.value.id)
qtemp = mw.wikibase.getBestStatements(geoRegion,geoSubRegion)[ct].mainsnak.datavalue.value.id or nil
if qtemp then
qlist=qlist..qtemp..',' -- add subregion Qvalue to the list
end
ct=ct+1
end
end
if qlist and #qlist>1 then -- create a table from the csv list
local _, c = qlist:gsub(",","")
local qtab=splitItem(qlist,c)
for xd,yd in pairs(qtab) do
yd=string.upper(yd)
if qset[yd] == nil and mw.wikibase.isValidEntityId(yd) then
dotmax=dotmax+1
qset[yd]=getQset (yd, (args['geo-data-type'] or 'P1082'), tostring(dotmax), args)
args['geo-data'..tostring(dotmax)] = yd
-- debugmsg(dotmax..' = '..(string.upper(yd) or 'Q Not found')..': '..(mw.wikibase.getLabel(yd) or 'label Not found')..': '..(mw.wikibase.getSitelink(yd) or 'link Not found'))
elseif not mw.wikibase.isValidEntityId(yd) then
debugmsg(mw.addWarning(yd..' is not a [[wikidata]] page. geo-data-list needs comma-separated Qvalues' ))
else debugmsg(mw.addWarning(yd..' is duplicate'))
end
end
end
-- qset contains set of Qvalues and can be from wikidata geo-region, list of geo-data-list Q values or individual geo-data1= etc
local ts = {} -- table to hold the keys
local useQset=false
for ky, val in pairs(qset) do
table.insert(ts, {key = ky, value = val[1], num=val[2]})
useQset=true
end
if useQset then
local order = string.lower(args['geo-list-sort'] or 'a') -- sort the list from previously stored sortkey, either Lowest or Highest first
if string.find(',al,vl,nl,wl,west,w,alpha,a,' , ','..order..',') then
table.sort(ts, function (a,b) return lessthan(a.value, b.value) end)
else
table.sort(ts, function (a,b) return morethan(a.value, b.value) end)
end
for ky,val in ipairs(ts) do -- assign values from sorted list to numbered parameter
args['numbered'..val.num] = ky -- The numbered parameter becomes the value for each dot that has a geo Qvalue, from manual, list or wiki sources.
end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) or args['geo-data'..x] then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,round(dotitem.lat,5),round(dotitem.lon,5),fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
if captionTable ~= '' and (not args['auto-caption']) then args['auto-caption']='on' end --default to include list if using geo values
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if negativeAnswer[autoOff] then captionTable='' end
if args.caption or (not negativeAnswer[autoOff]) then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then
table.insert(result,args.caption)
if captionTable ~= '' then table.insert(result,'<hr>') end
end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
if captionTable == '' then
capchk=true
else -- add the next line of content to the html table
capchk=false
end
end
end
if captionTable ~= '' then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
for k,v in pairs(captionList) do
local nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
local ngrp=v.value.group
if captionTableText[ngrp] then
-- debugmsg('table text for'..v.value.dotTag..', '..captionTableText[v.value.group][1]..', '..captionTableText[v.value.group][2]..', '..captionTableText[v.value.group][3]..' ('..captionTableText[v.value.group][4]..')')
local c1,c2
c1,c2=checkColors(shapeList[ngrp].shapeColor)
local cDot='<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;'
cDot=cDot..'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div>'
captionTable = htmlTableLine(captionTable,cDot,captionTableText[ngrp][1], captionTableText[ngrp][2], captionTableText[ngrp][3], captionTableText[ngrp][4])
end
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
-- local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<div class = "wikitable mw-collapsible ')
if string.find(autocaption,'collapsed') then table.insert(result,'mw-collapsed') end
table.insert(result,'" style="width:100%; line-height: 17px; border-style:none; text-align: center"><b>'..(args.toggletext or toggletext)..'</b>')
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if captionTable ~= '' then table.insert(result,captionTable..'</table>') end
if args.caption or (not negativeAnswer[autoOff]) then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
if maplist.width >= 400 and not(args['show-q-values']) then args['show-q-values'] = '1' -- default to on if wider than 400px
elseif maplist.width < 320 then args['show-q-values'] = '0' end -- turn off Qvalus if frame is too narrow, even if requested
return p._main(args)
end
return p
gqsmiqj591a951mg947o6gd8jfmzvze
204460
204459
2026-07-05T15:02:47Z
HaxelKaszëba
17492
204460
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it will be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext='Click for interactive fullscreen map with links to nearby articles'
local toggletext='[Hide/show caption list]'
local termsOfUse='Maps: terms of use'
local aboutOSM='About OpenStreetMaps'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]') or '0')
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x,y
x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.075*(math.abs(t.latbase)/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat,magVal)
if magVal and magVal>1 and magVal <=2 then zoom=zoom+(magVal-1) end
local dist=(156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom))/17
local y
if dist < 1 then
y=(round(dist*10,1))
return tostring(y*100)..'m', tostring(round(y*109,0))..'yds'
elseif dist <18 then
y=(round(dist,0))
return tostring(y)..'km', tostring(round(y*0.621371,1))..'miles'
elseif dist <500 then
y=round(dist/10)
return tostring(y*10)..'km', tostring(round(y*6.21371,0))..'miles'
else
y=round(dist/100)
return tostring(y*100)..'km', tostring(round(y*62.1371,0))..'miles'
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
-- debugmsg('making line for '..tostring(x)..'to '..tostring(result.markDest)..' with width '..tostring(result.mlWidth))
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
item=string.lower(multiCheck(args,'shape',argval,'image'))
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,'14px')
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
--sgd=dotTag attributes
shapeResult[nval].tagSize=tostring(shapeHeight*1.5)..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form TODO this must be possible in a single list!
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2)-1).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) ).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
if x1==x2 then table.insert(result,"transform: rotate(90deg);")
else table.insert(result,"transform: rotate("..tostring(math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi).."deg);\"></div>")
end
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:scale("..tostring(w/16)..", "..tostring(h/16)..")")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy-1 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
--debugmsg(row.labelText..', Align=right, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
--debugmsg(row.labelText..', Align=left, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,r=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = w/2+4,h/2+4
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1 offsety=-ty/2 +1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if r > widthzone and shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap)
-- debugmsg('makeLineTo line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..row.mlStyle..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '15')/100
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.1
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
--debugmsg('magVal='..magVal..', Magnify: H='..maplist.height..', ('..origH..'), W='..maplist.width..', ('..origW..'), scale='..scaleVal)
--debugmsg('top: '..tostring(((origW-maplist.width))/2)..'px;right: '..tostring(0-((origW-maplist.width))/2)..'px; (alt top: '..tostring(((origH-maplist.height))/2)..'px;)')
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb tleft">')
else table.insert(result,'<div class="thumb tright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
for indx in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,dotitem.lat,dotitem.lon,fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if args.caption or not negativeAnswer[autoOff] then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then table.insert(result,args.caption) end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
capchk=true
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<hr><div class="mw-customtoggle-'..myDivision..'">'..(args.toggletext or toggletext)..'</div>')
if string.find(autocaption,'collapsed') then
table.insert(result,'<div class="mw-collapsible mw-collapsed" id="mw-customcollapsible-'..myDivision..'">')
else
table.insert(result,'<div class="mw-collapsible" id="mw-customcollapsible-'..myDivision..'">')
end
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if args.caption or not negativeAnswer[autoOff] then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
return p._main(args)
end
return p
qvw2xergyo0d7mk9g1obxfaf679472g
204461
204460
2026-07-05T15:06:19Z
HaxelKaszëba
17492
204461
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it will be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext='Click for interactive fullscreen map with links to nearby articles'
local toggletext='[Hide/show caption list]'
local termsOfUse='Maps: terms of use'
local aboutOSM='About OpenStreetMaps'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]') or '0')
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x,y
x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.075*(math.abs(t.latbase)/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat, magVal)
zoom = tonumber(zoom) or 0
lat = tonumber(lat)
if not lat then
lat = 0 -- fallback
end
if magVal then
magVal = tonumber(magVal)
if magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y = round(dist * 10, 1)
return tostring(y * 100) .. 'm', tostring(round(y * 109, 0)) .. 'yds'
elseif dist < 18 then
y = round(dist, 0)
return tostring(y) .. 'km', tostring(round(y * 0.621371, 1)) .. 'miles'
elseif dist < 500 then
y = round(dist / 10)
return tostring(y * 10) .. 'km', tostring(round(y * 6.21371, 0)) .. 'miles'
else
y = round(dist / 100)
return tostring(y * 100) .. 'km', tostring(round(y * 62.1371, 0)) .. 'miles'
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
-- debugmsg('making line for '..tostring(x)..'to '..tostring(result.markDest)..' with width '..tostring(result.mlWidth))
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
item=string.lower(multiCheck(args,'shape',argval,'image'))
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,'14px')
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
--sgd=dotTag attributes
shapeResult[nval].tagSize=tostring(shapeHeight*1.5)..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
itemTab=splitItem(convertCoordsTrad (args['mark-coord'..argval]),2)
dotItem.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+")) or 0
else
dotItem.lat=tonumber(string.match(args['mark-lat'..argval],"[%.%-?%d]+")) or 0
dotItem.lon=tonumber(string.match(args['mark-lon'..argval],"[%.%-?%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form TODO this must be possible in a single list!
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2)-1).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) ).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
if x1==x2 then table.insert(result,"transform: rotate(90deg);")
else table.insert(result,"transform: rotate("..tostring(math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi).."deg);\"></div>")
end
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:scale("..tostring(w/16)..", "..tostring(h/16)..")")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy-1 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
--debugmsg(row.labelText..', Align=right, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
--debugmsg(row.labelText..', Align=left, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,r=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = w/2+4,h/2+4
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1 offsety=-ty/2 +1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if r > widthzone and shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap)
-- debugmsg('makeLineTo line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..row.mlStyle..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '15')/100
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.1
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
--debugmsg('magVal='..magVal..', Magnify: H='..maplist.height..', ('..origH..'), W='..maplist.width..', ('..origW..'), scale='..scaleVal)
--debugmsg('top: '..tostring(((origW-maplist.width))/2)..'px;right: '..tostring(0-((origW-maplist.width))/2)..'px; (alt top: '..tostring(((origH-maplist.height))/2)..'px;)')
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb tleft">')
else table.insert(result,'<div class="thumb tright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
for indx in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,dotitem.lat,dotitem.lon,fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if args.caption or not negativeAnswer[autoOff] then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then table.insert(result,args.caption) end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
capchk=true
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<hr><div class="mw-customtoggle-'..myDivision..'">'..(args.toggletext or toggletext)..'</div>')
if string.find(autocaption,'collapsed') then
table.insert(result,'<div class="mw-collapsible mw-collapsed" id="mw-customcollapsible-'..myDivision..'">')
else
table.insert(result,'<div class="mw-collapsible" id="mw-customcollapsible-'..myDivision..'">')
end
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if args.caption or not negativeAnswer[autoOff] then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
return p._main(args)
end
return p
d22uwjjuwgzn8pmgx2si9z0kbdmlsv5
204462
204461
2026-07-05T15:12:36Z
HaxelKaszëba
17492
204462
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it will be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext='Click for interactive fullscreen map with links to nearby articles'
local toggletext='[Hide/show caption list]'
local termsOfUse='Maps: terms of use'
local aboutOSM='About OpenStreetMaps'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]') or '0')
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t,r)
--[[ converts mercator projection longitude and latitude coordinates to x and y pixel coordinates, within a frame of given size, centre coordinates and zoom level.
t is a table of named indices: {lon, lat, lonbase, latbase, width, height, zoom}
output is two values, x and y, rounded to r decimal places--]]
local x,y
x=t.width/2 + ( ((math.rad(t.lon)*6378137) - (math.rad(t.lonbase)* 6378137) ) / (156543.03*math.cos(t.latbase/180)/(2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
y=t.height/2 + ( ( (math.log(math.tan(math.rad(t.latbase)/2+math.pi/4))*6378137) - (math.log(math.tan(math.rad(t.lat)/2+math.pi/4))*6378137) ) / (156543.03*math.cos(t.latbase/180) / (2^t.zoom) ) )*(1-(0.075*(math.abs(t.latbase)/90)))
return round(x,r),round(y,r)
--source: python code at https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames and https://wiki.openstreetmap.org/wiki/Mercator
--[[
width and height are the size, in pixels, of the map, which will be centerd around lonbase,latbase.
Method: Convert lon and lonbase to meter-offsets from coord(0,0), and subtract lonbase from lon,
zoom and latbase are used to scale the resulting meter-offset to pixels, and add it to width/2.
Convert lat and lat-base to meter-offsets from coord(0,0), subtract lat from latbase,
scale the resulting meter-offset to pixels, add it to height/2.
A correction factor '*(1-(0.075*(math.abs(t.latbase)/90) ) )' compensates for an error that seems to creep in towards
the edges of the map at higher latitudes. It was identified experimentally, and ensures a dot at the edge is
in the same place as if that location is positioned at the centre.
Original Python code for lat,lon to x,y where 0,0 is the centre of the map
print('x=',width+(((math.radians(lon1) * 6378137)-
(math.radians(lonbase) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom))),' y=',height+((
(math.log(math.tan(math.pi / 4 + math.radians(latbase) / 2)) * 6378137)-
(math.log(math.tan(math.pi / 4 + math.radians(lat1) / 2)) * 6378137))/
(156543.03*math.cos(latbase/180)/(2**zoom)))
) --]]
end
local function getScale(zoom, lat, magVal)
zoom = tonumber(zoom) or 0
lat = tonumber(lat)
if not lat then
lat = 0 -- fallback
end
if magVal then
magVal = tonumber(magVal)
if magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y = round(dist * 10, 1)
return tostring(y * 100) .. 'm', tostring(round(y * 109, 0)) .. 'yds'
elseif dist < 18 then
y = round(dist, 0)
return tostring(y) .. 'km', tostring(round(y * 0.621371, 1)) .. 'miles'
elseif dist < 500 then
y = round(dist / 10)
return tostring(y * 10) .. 'km', tostring(round(y * 6.21371, 0)) .. 'miles'
else
y = round(dist / 100)
return tostring(y * 100) .. 'km', tostring(round(y * 62.1371, 0)) .. 'miles'
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
-- debugmsg('making line for '..tostring(x)..'to '..tostring(result.markDest)..' with width '..tostring(result.mlWidth))
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
item=string.lower(multiCheck(args,'shape',argval,'image'))
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,'14px')
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
--sgd=dotTag attributes
shapeResult[nval].tagSize=tostring(shapeHeight*1.5)..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
local raw = args['mark-coord'..argval]
local lat, lon
lat, lon = string.match(raw, "{{coord|([%-%.%d]+)|([%-%.%d]+)}}")
if not lat or not lon then
lat, lon = string.match(raw, "([%-%.%d]+)%s*[,;]%s*([%-%.%d]+)")
end
if not lat or not lon then
local cleaned = convertCoordsTrad(raw)
if cleaned then
local tab = splitItem(cleaned, 2)
lat = tab[1] and string.match(tab[1], "[%-%.%d]+")
lon = tab[2] and string.match(tab[2], "[%-%.%d]+")
end
end
dotItem.lat = tonumber(lat) or 0
dotItem.lon = tonumber(lon) or 0
else
dotItem.lat = tonumber(string.match(args['mark-lat'..argval] or "", "[%-%.%d]+")) or 0
dotItem.lon = tonumber(string.match(args['mark-lon'..argval] or "", "[%-%.%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form TODO this must be possible in a single list!
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2)-1).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) ).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
if x1==x2 then table.insert(result,"transform: rotate(90deg);")
else table.insert(result,"transform: rotate("..tostring(math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi).."deg);\"></div>")
end
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:scale("..tostring(w/16)..", "..tostring(h/16)..")")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy-1 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
--debugmsg(row.labelText..', Align=right, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
--debugmsg(row.labelText..', Align=left, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,r=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = w/2+4,h/2+4
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1 offsety=-ty/2 +1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if r > widthzone and shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap)
-- debugmsg('makeLineTo line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..row.mlStyle..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '15')/100
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.1
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
--debugmsg('magVal='..magVal..', Magnify: H='..maplist.height..', ('..origH..'), W='..maplist.width..', ('..origW..'), scale='..scaleVal)
--debugmsg('top: '..tostring(((origW-maplist.width))/2)..'px;right: '..tostring(0-((origW-maplist.width))/2)..'px; (alt top: '..tostring(((origH-maplist.height))/2)..'px;)')
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb tleft">')
else table.insert(result,'<div class="thumb tright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
for indx in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,dotitem.lat,dotitem.lon,fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if args.caption or not negativeAnswer[autoOff] then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then table.insert(result,args.caption) end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
capchk=true
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<hr><div class="mw-customtoggle-'..myDivision..'">'..(args.toggletext or toggletext)..'</div>')
if string.find(autocaption,'collapsed') then
table.insert(result,'<div class="mw-collapsible mw-collapsed" id="mw-customcollapsible-'..myDivision..'">')
else
table.insert(result,'<div class="mw-collapsible" id="mw-customcollapsible-'..myDivision..'">')
end
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if args.caption or not negativeAnswer[autoOff] then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
return p._main(args)
end
return p
jtp84rcxtfdtxa8hjzphw3mtyy34vr6
204463
204462
2026-07-05T15:20:11Z
HaxelKaszëba
17492
204463
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it will be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext='Click for interactive fullscreen map with links to nearby articles'
local toggletext='[Hide/show caption list]'
local termsOfUse='Maps: terms of use'
local aboutOSM='About OpenStreetMaps'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]') or '0')
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t, r)
r = r or 2
if type(t) ~= "table" then
return 0, 0
end
local lon = tonumber(t.lon)
local lat = tonumber(t.lat)
local lonbase = tonumber(t.lonbase) or lon
local latbase = tonumber(t.latbase) or lat
local zoom = tonumber(t.zoom) or 0
local width = tonumber(t.width) or 0
local height = tonumber(t.height) or 0
if not lon or not lat then
return 0, 0
end
local cosBase = math.cos(math.rad(latbase))
if cosBase == 0 or cosBase ~= cosBase then
cosBase = 1
end
local scale = 156543.03 * cosBase / (2 ^ zoom)
if scale == 0 then
scale = 1
end
local x = width / 2 +
(
((math.rad(lon) * 6378137) - (math.rad(lonbase) * 6378137))
/ scale
) * (1 - (0.075 * (math.abs(latbase) / 90)))
local y = height / 2 +
(
((math.log(math.tan(math.rad(latbase) / 2 + math.pi / 4)) * 6378137)
- (math.log(math.tan(math.rad(lat) / 2 + math.pi / 4)) * 6378137))
/ scale
) * (1 - (0.075 * (math.abs(latbase) / 90)))
return round(x, r), round(y, r)
end
local function getScale(zoom, lat, magVal)
zoom = tonumber(zoom) or 0
lat = tonumber(lat)
if not lat then
lat = 0 -- fallback
end
if magVal then
magVal = tonumber(magVal)
if magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y = round(dist * 10, 1)
return tostring(y * 100) .. 'm', tostring(round(y * 109, 0)) .. 'yds'
elseif dist < 18 then
y = round(dist, 0)
return tostring(y) .. 'km', tostring(round(y * 0.621371, 1)) .. 'miles'
elseif dist < 500 then
y = round(dist / 10)
return tostring(y * 10) .. 'km', tostring(round(y * 6.21371, 0)) .. 'miles'
else
y = round(dist / 100)
return tostring(y * 100) .. 'km', tostring(round(y * 62.1371, 0)) .. 'miles'
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
-- debugmsg('making line for '..tostring(x)..'to '..tostring(result.markDest)..' with width '..tostring(result.mlWidth))
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
item=string.lower(multiCheck(args,'shape',argval,'image'))
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,'14px')
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
--sgd=dotTag attributes
shapeResult[nval].tagSize=tostring(shapeHeight*1.5)..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
local raw = args['mark-coord'..argval]
local lat, lon
lat, lon = string.match(raw, "{{coord|([%-%.%d]+)|([%-%.%d]+)}}")
if not lat or not lon then
lat, lon = string.match(raw, "([%-%.%d]+)%s*[,;]%s*([%-%.%d]+)")
end
if not lat or not lon then
local cleaned = convertCoordsTrad(raw)
if cleaned then
local tab = splitItem(cleaned, 2)
lat = tab[1] and string.match(tab[1], "[%-%.%d]+")
lon = tab[2] and string.match(tab[2], "[%-%.%d]+")
end
end
dotItem.lat = tonumber(lat) or 0
dotItem.lon = tonumber(lon) or 0
else
dotItem.lat = tonumber(string.match(args['mark-lat'..argval] or "", "[%-%.%d]+")) or 0
dotItem.lon = tonumber(string.match(args['mark-lon'..argval] or "", "[%-%.%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form TODO this must be possible in a single list!
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2)-1).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) ).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
if x1==x2 then table.insert(result,"transform: rotate(90deg);")
else table.insert(result,"transform: rotate("..tostring(math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi).."deg);\"></div>")
end
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:scale("..tostring(w/16)..", "..tostring(h/16)..")")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy-1 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
--debugmsg(row.labelText..', Align=right, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
--debugmsg(row.labelText..', Align=left, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,r=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = w/2+4,h/2+4
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1 offsety=-ty/2 +1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if r > widthzone and shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap)
-- debugmsg('makeLineTo line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..row.mlStyle..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '15')/100
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.1
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
--debugmsg('magVal='..magVal..', Magnify: H='..maplist.height..', ('..origH..'), W='..maplist.width..', ('..origW..'), scale='..scaleVal)
--debugmsg('top: '..tostring(((origW-maplist.width))/2)..'px;right: '..tostring(0-((origW-maplist.width))/2)..'px; (alt top: '..tostring(((origH-maplist.height))/2)..'px;)')
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb tleft">')
else table.insert(result,'<div class="thumb tright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
for indx in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local fgroup='F'..tostring(maplist.latbase+maplist.lonbase+ddots )
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,dotitem.lat,dotitem.lon,fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if args.caption or not negativeAnswer[autoOff] then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then table.insert(result,args.caption) end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
capchk=true
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<hr><div class="mw-customtoggle-'..myDivision..'">'..(args.toggletext or toggletext)..'</div>')
if string.find(autocaption,'collapsed') then
table.insert(result,'<div class="mw-collapsible mw-collapsed" id="mw-customcollapsible-'..myDivision..'">')
else
table.insert(result,'<div class="mw-collapsible" id="mw-customcollapsible-'..myDivision..'">')
end
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if args.caption or not negativeAnswer[autoOff] then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
return p._main(args)
end
return p
690638pnzsj710x250v5qs9jtm9s5dr
204464
204463
2026-07-05T15:26:39Z
HaxelKaszëba
17492
204464
Scribunto
text/plain
require('strict')
local delink=require('Module:Delink').delink
local getArgs = require('Module:Arguments').getArgs
local p = {}
local maplist={}
local sgNames={}
local highlightOption=false
local highlightNum
local visibleLinks
-- This module creates framed maps of anywhere in the world, at the required scale, and enables annotations,
-- dots, shapes, lines and other ways to customise the area of the map being shown. It also provides a link
-- to an interactive fullscreen version, which has locator dots instead of annotations and shapes.
-- This is the 2025 successor module to a wiki-markup template version of 2024, which itself was a successor
-- to the 'Graph'/VEGA driven template that was begun in 2016, until the Vega version was switched off in 2023.
-- This module is called from template {{OSM Location map}}, which uses the same parameter formats as before.
-- In addition it will be possible to use a more concise parameter format using the template {{OSM Location dots}}
-- In general the css output from the two formats will be identical, but the the concise version will allow bits of
-- greater control over some of the settings.
-- see the documentation on the two template pages for details of how to use the mapping features.
-- If language customisation is needed, there are text items below that can be translated. Also see the color table
-- below with details of how to add additional color names to allow localised alternatives.
-- (Translating other language shape-types could be possible, but has not currently been contemplated.
-- Parameter name translation would be harder but likely to be possible, ideally still retaining compatibility
-- with template calls already written using English).
local negativeAnswer={no=1,'0'-1,off=1}
local fullscreenlinktext='Click for interactive fullscreen map with links to nearby articles'
local toggletext='[Hide/show caption list]'
local termsOfUse='Maps: terms of use'
local aboutOSM='About OpenStreetMaps'
local shapeList={} --This sets up the 'factoryDefault' shape group 0
shapeList["0"]={shapeType="0",
Name="initialSettings",
Parent="0",
--sga items for the shape
shape="circle",
shapeSize="12px",
shapeColor="blue",
shapeAngle="0deg",
--sgb items for border of the shape
outlineWidth="0.5px",
outlineColor="darkblue",
outlineStyle="solid",
--sgc items text settings for labels
textSZ="11px",
textCL="darkgrey",
textNG="0deg",
--sgf further text settings
textSP="0px",
textLH="120%",
textOL="0px",
textBG="transparent",
--sgd items for dotTag text settings
tagSize="10px",
tagColor="white",
tagSpacer="0px",
tagAngle="0deg",
--sge items for extension line to connect label to dot
textEW="0px",
textEC="darkgrey",
textES="solid"
}
local colorList={} -- used by colorLookup to catch unsupported colors (eg 'LimeGreen'), to convert to generic version
colorList['green']='hardgreen' -- it could also be added to to include alternative language equivelants, for a quick solution.
colorList['red']='hardred' -- colorList ['source'] = target
colorList['white']='white' -- converts any color that includes 'source' into its equivelent target
colorList['blue']='hardblue' -- note, for translation you can add to this list, rather than replace it,
colorList['brown']='brown' -- which would mean existing map definitions in english would also still work, alongside translated ones
colorList['grey']='hardgrey'
colorList['gray']='hardgrey'
colorList['purple']='hardpurple'
colorList['orange']='hardorange'
colorList['leaf']='hardleaf'
--for a more thorough translation, you can add all the variants of the colors as further CTB elements and hex values or redirects
local CTB={} -- set up a table of color names (the CTB Color table index) and html hash colorhex values.
CTB["paleblue"],CTB["softblue"],CTB["hardblue"],CTB["darkblue"]="#D6E1EC","#77A1CB","#4B77D6","#1c559e"
CTB["palered"],CTB["softred"],CTB["hardred"],CTB["darkred"] = "#FCC6C0","#EC644B","#DB3123","#AA1205"
CTB["palegreen"],CTB["softgreen"],CTB["hardgreen"],CTB["darkgreen"]= "#D2F0E5","#81AF81","#269F46","#0b7527"
CTB["paleleaf"],CTB["softleaf"],CTB["hardleaf"],CTB["darkleaf"]= "#dff5c1","#b5e376","#8cc244","#679c21"
CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]= "#E8E8D6","#AAAA88","#777755","#444433"
CTB["palegray"],CTB["softgray"],CTB["hardgray"],CTB["darkgray"]=CTB["palegrey"],CTB["softgrey"],CTB["hardgrey"],CTB["darkgrey"]
CTB["palebrown"],CTB["softbrown"],CTB["hardbrown"],CTB["darkbrown"]="#FAF6ED","#CCB56C","#AD7F14","#754910"
CTB["palepurple"],CTB["softpurple"],CTB["hardpurple"],CTB["darkpurple"]="#e0d1e6","#c784e0","#a029cf","#7a05a8"
CTB["paleorange"],CTB["softorange"],CTB["hardorange"],CTB["darkorange"]="#ffedc2","#ffcf61","#EEB533","#e39f05"
CTB["black"],CTB["white"],CTB["yellow"]="#000000","#FFFFFF","#FAF039"
CTB["background"],CTB["paleground"],CTB["beigeground"]="#f9f6f2","#FEFEFA","#F5F5DC"
CTB["beige"]=CTB["beigeground"]
CTB["aqua"],CTB["teal"],CTB["fuchsia"] = "#00FFFF","#008080","#FF00FF"
CTB["maroon"],CTB["olive"],CTB["navy"] = "#800000","#808000","#000080"
CTB["lime"],CTB["limegreen"],CTB["aquamarine"] = "#00FF00","#32CD32","#7FFFD4"
CTB["silver"],CTB["yellow"],CTB["orchid"] = "#800000","#FFFF00","#DA70D6"
-- set up a table of predefined clip-paths
local pathshape={}
pathshape.squaredd = "M 19,1.25 l 0,18 -18,0 0,-18 18,0m-1,1 -16,0 0,16 16,0 0,-16m-1,1 0,14 -14,0 0,-14 14,0zm-1,1 -12,0 0,12 12,0 0,-12zm-1,1 0,10 -10,0 0,-10 10,0z"
pathshape.squared = "M 18,2.5 l 0,15 -15,0 0,-15 15,0m-1,1 -13,0 0,13 13,0 0,-13zm-1,1 0,11 -11,0 0,-11 11,0z"
pathshape.triangledd="M 0 20,20 20,10 0,0 20ZM1.5 19,10 1.7,18.5 19,1.5 19ZM3 18,17 18,10 3.8,3 18ZM4.5 17,10 5.4,15.4 17, 4.5,17ZM6 16,13.8 16,10 7.4z"
pathshape.triangled ="M1,18 l 18,0 l -9,-18 l -9,18zm1.7,-1.1 l 7.3,-14.6 l 7.3,14.6 l -14.6, 0zm1.7,-1 l 11.0,0 l -5.5,-11 l -5.5,11z"
pathshape.circledd = "M0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm0.8,0a9.2,9.2 0 1,1 18.4,0a9.2,9.2 0 1,1 -18.4,0m1,0a8.2,8.2 0 1,0 16.4,0a8.2,8.2 0 1,0 -16.4,0zm0.8,0a7.2,7.2 0 1,1 14.8,0a7.2,7.2 0 1,1 -14.8,0m1,0 a6.4,6.4 0 1,1 12.8,0a6.4,6.4 0 1,1 -12.8,0z"
pathshape.circled = "M2.5,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0m0.8,0 a5,5 0 1,1 11.4,0a5,5 0 1,1 -11.4,0"
pathshape.diamond = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10z"
pathshape.diamondd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm1,0 l 6,8.5 l 6,-8.5 -6,-8.5 -6,8.5zm1,0 l 5,-7 5,7 -5,7 -5,-7z"
pathshape.diamonddd = "M3,10 l 7,-10 l 7,10 -7,10 -7,-10zm0.75,0 l 6.25,9 l 6.25,-9 -6.25,-9 -6.25,9zm0.75,0 l 5.5,-8 5.5,8 -5.5,8 -5.5,-8zm0.75,0 l 4.75,7 l 4.75,-7 -4.75,-7 -4.75,7zm0.75,0 l 4,-6 4,6 -4,6 -4,-6z"
pathshape.crossd = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0zM2.3,10a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.cross = "M3.1,12.5 l4.2,0 l0,4.2 l5,0 l0,-4 l4.2,0 l0,-5 l-4.2,0 l0,-4.2 l-5,0 l0,4.2 l-4.2,0z"
pathshape.thincross = "M2,12 l6,0 l0,6 l4,0 l0,-6 l6,0 l0,-4 l-6,0 l0,-6 l-4,0 l0,6 l-6,0z"
pathshape.fivepointstar = "M10 0 L12.245 6.91 19.511 6.91 13.633 11.18 15.878 18.09 10 13.82 4.122 18.09 6.367 11.18 0.489 6.91 7.755 6.91Z"
pathshape.fivepointstard = "M10 1.5 L 11.90825 7.3735 18.08435 7.3735 13.08805 11.003 14.9963 16.8765 10 13.247 5.0037 16.8765 6.91195 11.003 1.91565 7.3735 8.09175 7.3735 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sixpointstar = "M10 0 L12.323 5.977 18.66 5 14.645 10 18.66 15 12.323 14.023 10 20 7.677 14.023 1.34 15 5.355 10 1.34 5 7.677 5.977Z"
pathshape.sixpointstard = "M10 1.5 L 11.97455 6.58045 17.361 5.75 13.94825 10 17.361 14.25 11.97455 13.41955 10 18.5 8.02545 13.41955 2.639 14.25 6.05175 10 2.639 5.75 8.02545 6.58045 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.sevenpointstar = "M10 0 L12.048 5.747 17.818 3.765 14.602 8.95 19.749 12.225 13.69 12.943 14.339 19.01 10 14.72 5.661 19.01 6.31 12.943 0.251 12.225 5.398 8.95 2.182 3.765 7.952 5.747Z"
pathshape.sevenpointstard = "M10 1.5 L11.7408 6.38495 16.6453 4.70025 13.9117 9.1075 18.28665 11.89125 13.1365 12.50155 13.68815 17.6585 10 14.012 6.31185 17.6585 6.8635 12.50155 1.71335 11.89125 6.0883 9.1075 3.3547 4.70025 8.2592 6.38495 ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.eightpointstar = "M10 0 L11.88 5.46 17.071 2.929 14.54 8.12 20 10 14.54 11.88 17.071 17.071 11.88 14.54 10 20 8.12 14.54 2.929 17.071 5.46 11.88 0 10 5.46 8.12 2.929 2.929 8.12 5.46Z"
pathshape.eightpointstard = "M10 0 L10 1.5 L11.598 6.141 16.01035 3.98965 13.859 8.402 18.5 10 13.859 11.598 16.01035 16.01035 11.598 13.859 10 18.5 8.402 13.859 3.98965 16.01035 6.141 11.598 1.5 10 6.141 8.402 3.98965 3.98965 8.402 6.141ZM0,10a10,10 0 1,0 20,0a10,10 0 1,0 -20,0zm1.5,0a8.5,8.5 0 1,1 17,0a8.5,8.5 0 1,1 -17,0z"
pathshape.ring="M2.6,9.5a7.5,7.5 0 1,0 15,0a7.5,7.5 0 1,0 -15,0zm1,0a6.5,6.5 0 1,1 13,0a6.5,6.5 0 1,1 -13,0z"
pathshape.boxd=pathshape.squared
pathshape.boxdd=pathshape.squaredd
pathshape.ellipsed=pathshape.circled
pathshape.ellipsedd=pathshape.circledd
local msg={}
local function debugmsg(txt)
table.insert(msg,txt)
end
local pmsg={}
local function previewMsg(txt)
table.insert(pmsg,txt)
end
local function colorLookup(color)
for c,l in pairs(colorList) do
if string.find(color,c) then return l end
end
return color
end
local function getColor (color,chk)
local c
local opacity="100"
if not color or color=='' then color='hardgrey' end
if color=="transparent" then return color end
if color=="background1" then color='background' end
if string.byte(color,1,1)==35 and (#color == 7 or #color == 9) then
c=color
elseif string.byte(color,1,1)==35 and #color == 4 then
c=string.sub(color,1,2)..'f'..string.sub(color,3,3)..'f'..string.sub(color,4,4)..'f'
else
local s=color..'1'
s= s:sub(0,s:find("%d")-1)
opacity=string.match(color,"%d+")
if not CTB[s] then s= colorList[s] end -- check for synonyms and translations
if not CTB[s] then debugmsg(mw.addWarning('color = '..color..'. The color name is not defined. Used default grey instead')) end
c=CTB[s] or CTB.hardgrey
end
if opacity and (tonumber(opacity) < 100) and string.find(c,"#")==1 and string.len(c)==7 and opacity~="" then
local hexval=string.format("%x",(math.floor(tonumber(opacity)*2.55)))
c=c..hexval
end
return c
end
function p.colorvalue(frame) -- enable external access to the CTB colorTable values. usage: {{#invoke:OSM Location map|colorvalue|color=hard blue}}
local c
if not frame.args.color or frame.args.color=='' then c='grey'
else c=string.lower(string.gsub(frame.args.color,'%s+','')) end
return string.upper(string.sub(getColor(c),2))
end
local function checkColors(color)
local c=getColor(color,'check')
local opacity =1 -- calculate colour brightness and return black or white for contrast
if c=='transparent' then return c,'#000000',0 end
if not (string.find(c,'#')==1) then return c,'#FFFFFF',0 end
if #c>8 then opacity= tonumber('0x'..(string.sub(c,8,9)))/500 end
local r=tonumber('0x'..(string.sub(c,2,3)))/255
local g=tonumber('0x'..(string.sub(c,4,5)))/255
local b=tonumber('0x'..(string.sub(c,6,7)))/255
if 0.2126 * r + 0.7152 * g + 0.0722 * b / opacity < 0.7 then
return c,'#FFFFFF',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
else return c,'#000000',0.2126 * r + 0.7152 * g + 0.0722 * b / opacity
end
end
local function morethan(a,b)
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]') or '0')
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
return a>b
end
local function lessthan(a,b)
if tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]')) then
a = tonumber(string.match(a, '%f[%d]%d[,.%d]*%f[%D]'))
b = tonumber(string.match(b, '%f[%d]%d[,.%d]*%f[%D]') or '0')
end
return a<b
end
local function getsize(size)
--size1 is between 1 and 3 values, each with px, equating to width,height,corner-rounding
--eg '15px 25px 5px' (spaces are optional) or '18px'. returns three numbers
local sizeval = {}
for v in string.gmatch(size, "[^px]+") do
table.insert(sizeval,v)
end
sizeval[1] = tonumber(sizeval[1]) or 13
sizeval[2] = tonumber(sizeval[2]) or sizeval[1]
sizeval[3] = tonumber(sizeval[3]) or 0
return sizeval[1],sizeval[2],sizeval[3]
end
local function coord2text(coord) -- looks through the output from {{coord}} to find the lat and long decimal values
-- and converts compass points to minus or not-minus, return with separating comma.
local lat = string.match(coord,'[%.%d]+°[NS]')
local lon = string.match(coord,'[%.%d]+°[EW]')
local neg={N="",S="-",W="-",E=""}
return neg[string.match(lat, '[NS]')]..string.match(lat,'[%.%d]+')..","..neg[string.match(lon, '[EW]')]..string.match(lon,'[%.%d]+')
end
local function convertCoordsTrad (row)
local coords=''
if row and string.find (row,'<span class="geo">') then
local a,b=string.find(row,'<span class="geo">')
local start=b+1
a,b=string.find(row,"</span>",b)
local finish=a-1
coords=string.sub(row,start,finish)
coords=string.gsub(coords,'; ',',')
end
return coords
end
local function convertCoords (row)
local start,finish,lat,lon,coords,says
if row then
local a,b=string.find(row,"<span class=")
start=a
while a do -- find the final span>
finish=b
a,b=string.find(row,"span>",b)
end
if start then
coords= string.sub(row,start,finish)
says=""
if string.find(coords,'<span class="error">') then
error("coord error: badly formed coordinates",0)
end
coords=coord2text(coords)
coords = string.sub(row,1,start-1)..coords..string.sub(row,finish+1)
else
coords=row
end
return coords
else
return "Nothing to see here"
end
end
local function fillCommas(val,max)
local line=''
if not val then line=',' -- ensure there is some content
else line = val --string.lower(string.gsub(val,"%s+","")) -- or strip spaces
end
if string.find(line,',') == 1 then line=' '..line end -- ensure initial comma is not skipped
local _, count=string.gsub(line,",","") -- add enough subsequent commas for all entries
line=line..string.rep(',',max-count)
while(string.find(line,",,") ) do
line=string.gsub(line,",,",", ,") --ensure string.gmatch doesn't ignore any empty items by padding with spaces
end
return line
end
local function makeLinkBox(left,top,wid,label, link)
local linkBoxName='Transparent square.svg'
if visibleLinks or '' =='yes' then linkBoxName='Red hollow square.svg' end
local builder = mw.html.create('div') --display:inline-block;
builder
:cssText('position:absolute;left:'..tostring(left-1-wid/2)..'px;top:'..tostring(top-1 + math.min(wid/2-12,0) - wid/2)..'px')
:wikitext(string.format( '[[File:%s|%dpx|link=%s|%s]]', linkBoxName, wid+2, link, label ))
return tostring(builder)
end
local function extractItem(row,searchItem)
-- remove text following a searchItem or start of line, which might be in quote-marks to allow commas
local xend,xstart=1,0
if not row then return '','' end
if searchItem then xend,xstart= string.find(row or '',searchItem or 'image:') end
if not xstart then return string.gsub(string.gsub(row or '',"%b\"\"", ''),"%b\'\'", '') or '','' end
while row:byte(xstart+1) == 32 and xstart<#row do -- skip over any leading spaces
xstart=xstart+1
end
local xbyte=row:byte(xstart+1)
if xbyte == 34 or xbyte == 39 then -- are they wrapped in single or double quotes
xstart=xstart+1
xend=row:find(string.char(xbyte),xstart+1)
else
xend = row:find(',',xstart+1) -- if no quotes, we assume no commas
if not xend then xend=#row+1 end
end -- return residual row and extracted text
return row:sub(0,xstart)..row:sub(xend), row:sub(xstart+1,xend-1)
end
local function itemCheck(item,ext)
if not item then return nil end
if not ext then ext='' end
return (string.match(item,"[%.%-?%d]+") or '0')..ext
end
local function stripdivs(line)
return string.gsub(line or '',"%b<>", ' ')
end
local function splitItem(item,max) -- takes a commas-sep list and returns a table of lowercase items with no spaces, or nil
local r={}
local x=1
item=string.lower(fillCommas(item,max))
for t in string.gmatch(item,"[^,]+") do
r[x]=string.gsub(t,"%s+","")
if r[x]=='' then r[x]= nil else -- residual items might have commas
if x>max then r[max]=(r[max] or '')..', '..r[x] end
end
x=x+1
end
return r
end
local function ParseShapeTypes (result,args,sgval) -- for use with compressed, comma-separated 'sg plus dots' parameters
--[[ shape table items and default values as set at top of page
shapeType="0", Name="initialSettings", Parent="0",
--sga items for the shape shape="circle", shapeSize="12px", shapeColor="blue", shapeAngle="0deg",
--sgb items for border outlineWidth="0.5px", outlineColor="darkblue", outlineStyle="solid",
--sgc items label text textSZ="11px", textCL="white", textNG="0deg", textAT=attributes ("bold" and/or "italic")
--sgd items for dotTag tagSize="11px", tagColor="darkgrey", tagSpacer="0px", tagAngle="0deg",
--sge extension line textEW="1px", textEC="darkblue", textES="solid"
--sgf fx for text labels textSP="0px" textLH="100%" textOL="1px", textBG="paleground",
<!--| sga = shape,Size,Color,Angle|sgb= outlineWidth,color,style eg: sga1=circle,14px,blue,0deg| sgb1=0px,dark grey,solid
| sgc=textSize,color,angle,bold/italic | sgd=tagSize,Color,Spacer,Angle eg: sgc1=11px,dark grey,0deg,normal| sgd1=9px,white,0px,0deg
| sge=lineWidth,color,style |sgf=textspacing,lineHeight%,outlinepx,backgroundcolor, [bold,italic] eg: sge1=0px,black, solid| sgf1=0px,120%,0px,background
| dot=shape-group/lat/lon/title/dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
--]]
if args["sgn"..sgval] then
local sgname=string.match(args["sgn"..sgval],"(%w+)(.*)")
sgNames[sgname]=sgval
end
local parent= args["sgp"..sgval]
if parent then
parent=string.match(parent,"(%w+)(.*)")
local pos= string.find(parent,"%d+")
if pos == 1 then
parent=string.match(parent,"%d+")
else
parent=sgNames[parent] or '1'
end
end
if sgval~='H' then
if not parent or tonumber(parent) > tonumber(sgval) then
if sgval=="1" then parent="0" else parent="1" end
end
end
local itemTab, line, filename
result[sgval]={}
result[sgval].shapeType=sgval
line,filename=extractItem(args['sga'..sgval] or '','image:')
if sgval=='1' and not args.sga1 then line='circle,12px,blue,0deg' end -- ensure there is a parent=1 sga
result[sgval].shapeFile=filename or ''
-- sga= Attributes for shape
itemTab=splitItem(line,4)
result[sgval].shape = itemTab[1] or result[parent].shape
result[sgval].shapeSize=itemTab[2] or result[parent].shapeSize
result[sgval].shapeColor=itemTab[3] or result[parent].shapeColor
result[sgval].shapeAngle=itemCheck(itemTab[4],'deg') or result[parent].shapeAngle
-- sgb= Border outline attributes for shape
itemTab=splitItem(args['sgb'..sgval],3)
result[sgval].outlineWidth=itemCheck(itemTab[1],'px') or result[parent].outlineWidth
result[sgval].outlineColor=itemTab[2] or result[parent].outlineColor
result[sgval].outlineStyle=itemTab[3] or result[parent].outlineStyle
--sgc=character attributes for label
itemTab=splitItem(args['sgc'..sgval],4)
result[sgval].textSZ=itemCheck(itemTab[1],'px') or result[parent].textSZ -- size of text in px
result[sgval].textCL=itemTab[2] or result[parent].textCL -- colour for text
result[sgval].textNG=itemCheck(itemTab[3],'deg') or result[parent].textNG -- Angle for text
result[sgval].textAT=itemTab[4] or result[parent].textAT -- attributes bold, and/or italic
--sgd=dotTag attributes
itemTab=splitItem(args['sgd'..sgval],4)
result[sgval].tagSize=itemCheck(itemTab[1],'px') or result[parent].tagSize
result[sgval].tagColor=itemTab[2] or result[parent].tagColor
result[sgval].tagSpacer=itemCheck(itemTab[3],'px') or result[parent].tagSpacer
result[sgval].tagAngle=itemCheck(itemTab[4],'deg') or result[parent].tagAngle
--sge= extension line attributes
itemTab=splitItem(args['sge'..sgval],4)
result[sgval].textEW=itemCheck(itemTab[1],'px') or result[parent].textEW -- width
result[sgval].textEC=itemTab[2] or result[parent].textEC -- colour
result[sgval].textES=itemTab[3] or result[parent].textES -- style
--sgf= fx for label text
itemTab=splitItem(args['sgf'..sgval],4)
result[sgval].textSP=itemCheck(itemTab[1],'px') or result[parent].textSP -- spacing value for letters
result[sgval].textLH=itemCheck(itemTab[2],'%') or result[parent].textLH -- Angle for text
result[sgval].textOL=itemCheck(itemTab[3],'px') or result[parent].textOL -- width of text-border line
result[sgval].textBG=itemTab[4] or result[parent].textBG -- color for text background
return result
end
local function round(x,dec)
-- x=number [, dec=integer] returns numeric value with upto dec decimals (all but first trailing zeros get truncated)
if (dec or 0)==0 then
return x>=0 and math.floor(x+0.5) or math.ceil(x-0.5) --this avoids .0 where dec=0
end
dec =10^(dec)
return x>=0 and math.floor(x*dec+0.5)/dec or math.ceil(x*dec-0.5)/dec
end
local function maptogrid(t, r)
r = r or 2
if type(t) ~= "table" then
return 0, 0
end
local lon = tonumber(t.lon)
local lat = tonumber(t.lat)
local lonbase = tonumber(t.lonbase) or lon
local latbase = tonumber(t.latbase) or lat
local zoom = tonumber(t.zoom) or 0
local width = tonumber(t.width) or 0
local height = tonumber(t.height) or 0
if not lon or not lat then
return 0, 0
end
local cosBase = math.cos(math.rad(latbase))
if cosBase == 0 or cosBase ~= cosBase then
cosBase = 1
end
local scale = 156543.03 * cosBase / (2 ^ zoom)
if scale == 0 then
scale = 1
end
local x = width / 2 +
(
((math.rad(lon) * 6378137) - (math.rad(lonbase) * 6378137))
/ scale
) * (1 - (0.075 * (math.abs(latbase) / 90)))
local y = height / 2 +
(
((math.log(math.tan(math.rad(latbase) / 2 + math.pi / 4)) * 6378137)
- (math.log(math.tan(math.rad(lat) / 2 + math.pi / 4)) * 6378137))
/ scale
) * (1 - (0.075 * (math.abs(latbase) / 90)))
return round(x, r), round(y, r)
end
local function getScale(zoom, lat, magVal)
zoom = tonumber(zoom) or 0
lat = tonumber(lat)
if not lat then
lat = 0 -- fallback
end
if magVal then
magVal = tonumber(magVal)
if magVal > 1 and magVal <= 2 then
zoom = zoom + (magVal - 1)
end
end
local dist = (156543.03 * math.cos(math.rad(math.abs(lat))) / (2 ^ zoom)) / 17
local y
if dist < 1 then
y = round(dist * 10, 1)
return tostring(y * 100) .. 'm', tostring(round(y * 109, 0)) .. 'yds'
elseif dist < 18 then
y = round(dist, 0)
return tostring(y) .. 'km', tostring(round(y * 0.621371, 1)) .. 'miles'
elseif dist < 500 then
y = round(dist / 10)
return tostring(y * 10) .. 'km', tostring(round(y * 6.21371, 0)) .. 'miles'
else
y = round(dist / 100)
return tostring(y * 100) .. 'km', tostring(round(y * 62.1371, 0)) .. 'miles'
end
end
local function ParseData (args,dotval) -- for use with compressed, comma-separated 'sg plus dots' parameters
-- takes a structured series of comma-separated items which get parsed as the following:
-- dot(n)= (sgNumber or Name),{{coord}} or (lat,lon), (dotTag)
-- dotlink(n) = single-parameter text to give wikilink and/or title used by tootlip, fullscreen dots and autocaption list
-- dotlabel(n) = 'label text',pos(left,roght,top,bottom,centre,auto),(dx), (dy) pixel offsets, params, info
-- dotpic(n) = single parameter wikimedia filename for an image to use in photopanel and/or fullscreen dots
-- dotfeature(n)= 'mark-line' (,linewidth,style,gap) or 'photo-panel' (,image-dim,panel-width,panel-height), draws line to n-1
-- label is used if either a position and/or an x,y offset are not 0,0 ( if no label then dotTag will be put at at the x,y offset, or over the dot
-- label text can be autoaligned if x,y puts it left or right of the dot, or centered if above/below)
-- quote marks are not needed unless including commas within the label text
-- param1 is optional items separated by spaces, and can include [nolabel nolist nomap hemisphere+1 hemisphere-1]
-- info is free wikitext, to be used in the fullscreen dot box. (use dotpic to show a picture)
--<!--| dotx=shape-group,[lat,lon or {{coord}} ], dotTag | dotlink=link or tooltip | dotlabel=label,position,dx,dy,param1,info| dotpic=filename-->
local result={}
local count=1
local row = convertCoords (args["dot"..dotval]) -- swap in any {{coord}} values so they are csv lat and lon
row=fillCommas(row,4)
result.code=dotval -- store the parameter name as the id code
for item in string.gmatch(row,"[^,]+") do -- iterate through 'row', adding each csv item in turn, if present
if count==1 then --see if it is a number or a name
local pos= string.find(item,"%d+")
if pos == 1 then
result.group=string.lower(string.gsub(item,"%s+",""))
else
item=string.match(item,"(%w+)(.*)") -- ensure just a single word
result.group=sgNames[item]
end
elseif count==2 then
result.lat=tonumber(string.match(item,"[%.%-?%d]+")) or 0-- find the number, with no non-numeric stuff
elseif count==3 then
result.lon=tonumber(string.match(item,"[%.%-?%d]+")) or 0
elseif count==4 then
result.dotTag=item:match( "^%s*(.-)%s*$" ) or "" -- dotTag allows for internal spaces, but no commas
end
count=count+1
end
row, result.labelText= extractItem(args["dotlabel"..dotval])
result.labelText= string.gsub(result.labelText,"[%^]+","<br>") -- convert hats to line breaks
local item=splitItem(row,6)
result.labelPos=item[2] or 'center'
result.dx=tonumber(string.match(item[3] or '0',"[%.%-?%d]+")) or 0
result.dy=tonumber(string.match(item[4] or '0',"[%.%-?%d]+")) or 0
result.param1=string.lower(item[5] or '')
if string.find(result.param1,"hemisphere-1",1,true) then result.lon=result.lon-360
elseif string.find(result.param1,"hemisphere+1",1,true) then result.lon=result.lon+360
end
local txt = ''
if item[6] then -- ensure all info elements are included, including commas
count=1
local max=6
for t1 in string.gmatch(fillCommas(row,max),"[^,]+") do
if count>max then txt=txt..',' end
if count>=max then txt=txt..t1 end
count=count+1
end
end
result.info=txt
result.imageName = args['dotpic'..dotval]
-- Get first wikilinked item (if any) from the args.dotlink and set this plus the delinked text
local testx=args["dotlink"..dotval] or ''
result.dotLink=testx
if testx ~= '' then
testx=stripdivs(testx)
result.title=delink({ testx })
local linkstart= string.find(testx,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
result.dlink=delink( { string.sub(testx,linkstart,string.find(testx,']]',1,true)+1),wikilinks='target' } )
else result.dlink=''
end
else
result.dlink=''
result.title=''
end
if args['dotfeature'..dotval] then
local item=splitItem(args['dotfeature'..dotval],6)
if (item[1] or '') =='photo-panel' then
result.ppwidth= tonumber(string.match((item[3] or '110'),"%d+")) -- panel width
result.ppheight= tonumber(string.match((item[4] or '48'),"%d+") ) --panel height
result.photowidth=round(tonumber(string.match((item[2] or '1.3'),"[%.%-?%d]+")) * result.ppheight+1,0) -- dimension to set image size
result.photoImage=result.imageName
result.posType='photo-panel'
elseif (item[1] or '') == 'mark-line' then
local x=tonumber(dotval or '0')
result.markDest=item[5] or tostring(x-1)
result.mlWidth= tonumber(string.match((item[2]) or '',"%d+") or '1')
result.mlStyle= item[3] or 'solid'
result.mlGap=tonumber(string.match((item[4] or ''),"[%d]+") or '0')
result.posType='mark-line'
-- debugmsg('making line for '..tostring(x)..'to '..tostring(result.markDest)..' with width '..tostring(result.mlWidth))
end
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
maplist.lon=result.lon
maplist.lat=result.lat
result.gridx, result.gridy = maptogrid(maplist,1) -- convert geo coords to grid xy - using values from maplist table
return result
end
local function multiCheck (args, argName, argVal, defVal, alt)
if not alt then alt='nonexistant' end
if argVal=='H' and not args[argName..'H'] then argVal=highlightNum or '1' end
if argVal=='' then
return (args[argName] or args[alt] or (args[argName..'D']) or args[alt..'D'] or defVal) -- unnumbered args do not inherit from D or 1
else
return (args[argName..argVal]) or (args[alt..argVal]) or (args[argName..'D']) or (args[alt..'D']) or (args[argName..'1']) or (args[alt..'1']) or defVal
end
end
local function assignTradstyleShape(shapeResult,default,dotResult,args,nval)
local item,itemTab
local autoDotTag=''
local shapeWidth,shapeHeight=0,0
local argval=nval -- to catch the unnumbered shape series
if argval=='0' then argval='' end
if nval=='H' then shapeResult.H={} end
item=string.lower(multiCheck(args,'shape',argval,'image'))
if string.find(item,'n-',0,true)==1 or string.find(item,'l-',0,true)==1 then
autoDotTag=string.sub(item,0,1)
item=string.sub(item,3)
end
if item == 'image' then
shapeResult[nval].shape = 'image:'
shapeResult[nval].shapeFile =multiCheck(args,'mark',argval,'Red pog.svg')
shapeWidth=-1
else shapeResult[nval].shape = item
end
item= multiCheck(args,'mark-size',argval,'14px')
local a,b,c= getsize(string.gsub(string.gsub(item,',','px')..'px','pxpx','px'))
if b==a and args['mark-dim'..argval] then
b= b / tonumber(string.match(args['mark-dim'..argval],"[%.%-?%d]+"))
end
shapeHeight=b/2
item=tostring(a)..'px'..tostring(b)..'px'..tostring(c)..'px'
shapeResult[nval].shapeSize= item
itemTab=splitItem(multiCheck(args,'shape-color',argval,'hard red'),2)
shapeResult[nval].shapeColor=itemTab[1] or 'hardred'
item=itemCheck(itemTab[2],'%') -- jump through the various opacity hoops and add to color if needed
if not item then item=itemCheck(args['shape-opacity'..argval],'%') end
if item and item~='0%' and item~='100%' then shapeResult[nval].shapeColor=shapeResult[nval].shapeColor..item end
shapeResult[nval].shapeAngle=itemCheck(multiCheck(args,'shape-angle',argval,'0'),'deg') or '0deg'
--sort out the outline entry
itemTab=splitItem(multiCheck(args,'shape-outline',argval,'transparent,0,100,solid'),4)
shapeResult[nval].outlineColor=itemTab[1] or 'dark grey'
shapeResult[nval].outlineWidth=itemCheck(itemTab[2],'px') or '1px'
if itemTab[3] and itemTab[3]~='100' and itemTab[3]~='0' then
shapeResult[nval].outlineColor=shapeResult[nval].outlineColor..itemCheck(itemTab[3],'%')
end
shapeResult[nval].outlineStyle=itemTab[4] or 'solid'
-- label size, background, outline
itemTab=splitItem( multiCheck(args,'label-size',argval,'12'),3)
shapeResult[nval].textSZ=itemCheck(itemTab[1],'px') or '12px'
if itemTab[2]=='outline' then
shapeResult[nval].textBG=itemTab[3] or 'transparent'
shapeResult[nval].textOL='1px'
elseif itemTab[3]=='outline' then
shapeResult[nval].textBG=itemTab[2] or 'transparent'
shapeResult[nval].textOL='1px'
else shapeResult[nval].textOL='0px'
shapeResult[nval].textBG=itemTab[2] or 'transparent'
end
if getColor(shapeResult[nval].textBG)==CTB['hardgrey'] and shapeResult[nval].textBG~='hardgrey' then shapeResult[nval].textBG= 'transparent' end
--label color etc
itemTab=splitItem(multiCheck(args,'label-color',argval, 'darkgrey','label-colour'),2)
shapeResult[nval].textCL=itemTab[1] or 'darkgrey'
if itemTab[2] and itemTab[2]~='0%' and itemTab[2]~='100%' then shapeResult[nval].textCL=shapeResult[nval].textCL..itemTab[2] end
shapeResult[nval].textSP=itemCheck( multiCheck(args,'label-spacing',argval,'0'),'px') -- sets letter-spacing in px
shapeResult[nval].textLH=itemCheck( multiCheck(args,'label-height',argval,'120'),'%') -- sets line height, 120% default
shapeResult[nval].textNG=itemCheck(multiCheck(args,'label-angle',argval,'0'),'deg')
--sgd=dotTag attributes
shapeResult[nval].tagSize=tostring(shapeHeight*1.5)..'px'
local c1,c2=checkColors(shapeResult[nval].shapeColor)
shapeResult[nval].tagColor=c2
shapeResult[nval].tagSpacer='0px'
shapeResult[nval].tagAngle='0deg'
-- sge extension line attributes
local shapePos=splitItem(multiCheck(args,'label-pos',argval,'right'),6)
if shapePos[2]=='with-line' or shapePos[2]=='n-line' then
shapeResult[nval].textEW=(shapePos[3] or '1')..'px' -- width
shapeResult[nval].textEC=(shapePos[4] or shapeResult[nval].shapeColor or 'darkgrey')
elseif shapePos[2]=='photo-panel' then
shapeResult[nval].textEW='2px' -- width
shapeResult[nval].textEC=shapeResult[nval].textCL
else
shapeResult[nval].textEW='0px' -- width
shapeResult[nval].textEC='grey'-- colour
end
shapeResult[nval].textES='solid'
if argval=='H' then return dotResult end
--Assign dot values
local dotItem={}
dotItem.group=nval
dotItem.code=nval
dotItem.posType=shapePos[2] or 'nil'
if (shapePos[2] or '') =='photo-panel' then
dotItem.ppwidth= tonumber(string.match((shapePos[4] or '110'),"%d+"))
dotItem.ppheight= tonumber(string.match((shapePos[5] or '48'),"%d+") )
dotItem.photowidth=round(tonumber(string.match((shapePos[3] or '1.3'),"[%.%-?%d]+")) * dotItem.ppheight+1,0)
dotItem.photoImage=args['mark-image'..argval]
--debugmsg('photo-panel, '..shapePos[2]..', 3='..shapePos[3]..', 4='..shapePos[4]..', 5='..(shapePos[5] or '(48')..'photowidth='..tostring(dotItem.photowidth))
end
if (shapePos[2] or '') =='mark-line' then
local x=tonumber(argval or '1')
dotItem.markDest=shapePos[6] or tostring(x-1)
dotItem.mlWidth= tonumber(string.match((shapePos[3] or '1'),"%d+"))
dotItem.mlStyle= shapePos[4] or 'solid'
dotItem.mlGap=tonumber(string.match((shapePos[5] or '0'),"[%d]+"))
shapeResult[nval].textEC=shapeResult[nval].outlineColor or 'darkgrey'
end
if args['mark-coord'..argval] then
local raw = args['mark-coord'..argval]
local lat, lon
lat, lon = string.match(raw, "{{coord|([%-%.%d]+)|([%-%.%d]+)}}")
if not lat or not lon then
lat, lon = string.match(raw, "([%-%.%d]+)%s*[,;]%s*([%-%.%d]+)")
end
if not lat or not lon then
local cleaned = convertCoordsTrad(raw)
if cleaned then
local tab = splitItem(cleaned, 2)
lat = tab[1] and string.match(tab[1], "[%-%.%d]+")
lon = tab[2] and string.match(tab[2], "[%-%.%d]+")
end
end
dotItem.lat = tonumber(lat) or 0
dotItem.lon = tonumber(lon) or 0
else
dotItem.lat = tonumber(string.match(args['mark-lat'..argval] or "", "[%-%.%d]+")) or 0
dotItem.lon = tonumber(string.match(args['mark-lon'..argval] or "", "[%-%.%d]+")) or 0
end
if args['dateline'..argval] and (args['dateline'..argval]=='1' or args['dateline'..argval]=='-1') then
dotItem.lon=dotItem.lon+(tonumber(args['dateline'..argval] ) *360)
end
maplist.lon=dotItem.lon
maplist.lat=dotItem.lat
dotItem.gridx, dotItem.gridy = maptogrid(maplist,1)
local item=args['mark-title'..argval] or '' -- sort out the caption, wikilink and plaintext tooltip items from dotLink
if item=='none' then dotItem.param1='nomap nolist' item='' end
dotItem.dotLink=item
if item ~= '' then
item=stripdivs(item)
dotItem.title=delink({item})
local linkstart= string.find(item,'[[',1,true) -- use true to ensure a plain search (no pattern)
if linkstart then
dotItem.dlink=delink({string.sub(item,linkstart,string.find(item,']]',1,true)+1),wikilinks='target'})
else dotItem.dlink=''
end
else
dotItem.dlink=''
dotItem.title=''
end
if autoDotTag=='n' then item=nval
elseif autoDotTag=='l' then item=string.char(64+tonumber(nval))
else item='' end
dotItem.dotTag = args['numbered'..argval] or item
if shapePos[2]=='n-line' and (args['label'..argval] or args['label'..argval]=='') then
if dotItem.dlink =='' then
item=dotItem.dotTag..' '..args['label'..argval]
else
item='[['..dotItem.dlink..'|'..dotItem.dotTag..']] '..args['label'..argval]
end
else item=(args['label'..argval] or '') end
if args['labela'..argval] then item = item..'^'..args['labela'..argval] end
if args['labelb'..argval] then item = item..'^'..args['labelb'..argval] end
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
dotItem.labelText = a -- convert hats to line breaks
if argval=='' then item = (args['label-offset-x']) or (args.ldx) or '0'
else item=args['label-offset-x'..argval] or args['ldx'..argval] or args['label-offset-xD'] or args.ldxD or args['label-offset-x1'] or args.ldx1 or '0'
end
dotItem.dx=tonumber(string.match(item,"[%.%-?%d]+"))
if argval=='' then item = (args['label-offset-y']) or (args.ldy) or '0'
else item=args['label-offset-y'..argval] or args['ldy'..argval] or args['label-offset-yD'] or args.ldyD or args['label-offset-y1'] or args.ldy1 or '0'
end
dotItem.dy=tonumber(string.match(item,"[%.%-?%d]+"))
dotItem.labelPos=shapePos[1]
if args['mark-image'..argval] then dotItem.imageName= args['mark-image'..argval] end
dotItem.info=args['mark-description'..argval] or ''
table.insert(dotResult,1,dotItem) -- add to start of list, so they are in reverese order for displaying
return dotResult
end
local function tradstyleParseShapes(args,dotTable,dotmax)
local sgNumbers,sgSortable={},{}
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form TODO this must be possible in a single list!
if args['mark-coord'] or (args['mark-lat'] and args['mark-lon']) then table.insert(sgSortable,'0') end -- add it to the end of the list
local default={} default.D={}
local dotResult={}
for k,sgnum in pairs(sgSortable) do -- work through the sorted list, parsing each set of shapes in turn, from 1 upwards
shapeList[sgnum]={}
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,sgnum)
end
dotTable=assignTradstyleShape(shapeList,default,dotTable,args,'H') -- construct an extra highlight shapeitem
return dotTable
end
local function checkfortooltip (title,dx,dy,dotlabel,dlink,nolabel) -- returns tlink if available, and dlink, if needed and tshape=true if shape needed
local tshape,tlink = false,""
if dlink~='' and not nolabel then tlink=dlink end
if (tlink=="" or nolabel) and title~="" then tshape=true end -- tshape flags True if title is wanted for shape
if (not (dx==0 or dy==0) or dotlabel=='') and title~='' then tshape=true end -- add tooltip to shape if its number has moved
return tshape,tlink
end
local function tshift(angle) -- adjustment to place text near the centre of a triangle, shifted to allow rotation of triangle shape
local x=tonumber(string.match(angle,"%-?%d+"))
if x<0 then x=360+x end -- set to a single degree direction, 0 to 360
if x>359 then return 0,0 end
-- shift the centre of the triangle based on rotation value
if x <=40 or x>=320 then return -0.17,0 -- triangle up= -shiftv
elseif x>=140 and x<=220 then return 0.17,0 --triangle down= +shiftv
elseif x >220 then return 0,-0.17 --triangle left= -shifth
elseif x >40 then return 0,0.22 --triangle right= +shifth
end
return 0,0
end
local function makeTriangle(result,row,shape,outline,tlink)
local w,h,r=getsize(shape.shapeSize)
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
w=w+p*2
h=h+p*2
end
table.insert(result,'<div ')
if tlink then
table.insert(result,' title="'..row.title..'" ')
end
table.insert(result,'style="display:inline-block; position: absolute')
if shape.shapeAngle ~= '0deg' then
table.insert(result,'; transform: rotate('..shape.shapeAngle..')')
end
local shiftv,shifth=0,0
shiftv,shifth=tshift(shape.shapeAngle)
table.insert(result,'; top: '..tostring(row.gridy-h/2+h*shiftv)..'px')
table.insert(result,'; left: '..tostring(row.gridx-w/2+w*shifth)..'px; width: 0; height: 0; outline-width: 0px')
table.insert(result,'; border-left: '..tostring(w/2)..'px solid transparent')
table.insert(result,'; border-right: '..tostring(w/2)..'px solid transparent')
if outline then -- fill with outline colour, to make a 'base layer' or shape colour
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.outlineColor).. '">')
else
table.insert(result,'; border-bottom: '..tostring(h)..'px solid '..getColor(shape.shapeColor).. '">')
end
table.insert(result,'</div>')
end
local function makeSquare(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize)
if shape.shape=='square' then h=w end -- squares are always square!. box can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
if r~=0 then div:css('border-radius',tostring(r).."px") end
if shape.shape=='panel' then
div:css('top', tostring(row.gridy).."px")
div:css('left', tostring(row.gridx).."px")
else
div:css('top', tostring(row.gridy-h/2).."px")
div:css('left', tostring(row.gridx-w/2).."px")
end
div:css('width', tostring(w).."px")
div:css('height', tostring(h).."px")
div:css('background-color', getColor(shape.shapeColor) )
div:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeCircle(result,row,shape,tshape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
if shape.shape=='circle' then h=w end -- circles are always round. ellipse can stretch
local div=mw.html.create ('div')
if tshape then -- Add tooltip if needed
div:attr('title',row.title)
end
div:css('position', "absolute")
if shape.outlineWidth ~= "0px" then
div:css('outline', shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
end
if shape.shapeAngle ~= "0deg" then
div:css('transform',"rotate("..shape.shapeAngle..")")
end
div
:css('top', tostring(row.gridy-h/2).."px")
:css('left', tostring(row.gridx-w/2).."px")
:css('width', tostring(w).."px")
:css('height', tostring(h).."px")
:css('border-radius', "50%")
:css('background-color', getColor(shape.shapeColor) )
:css('color', 'inherit')
table.insert(result,tostring(div))
end
local function makeRuleA(result,row,shape)
local w,h,r=getsize(shape.shapeSize) -- = width,height,rounding
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local lineV=0
if shape.shape=='rulea' then lineV=oWid*3+16 end
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- create a square transparent container, which will rotate line and arrow together
table.insert(result,"; top:"..tostring(row.gridy - w/2).."px")
table.insert(result,"; left:"..tostring(row.gridx - w/2).."px")
table.insert(result,"; width:"..tostring(w).."px")
table.insert(result,"; height:"..tostring(w).."px; background:transparent; color:inherit;")
table.insert(result,"; transform: rotate( "..tostring(tonumber(string.match(shape.shapeAngle,"[%.%-?%d]+")) - 90).."deg);\">" )
table.insert(result,"<div style=\"display:inline-block; position: absolute") -- put the line (as a border-right) across the container
table.insert(result,"; top:"..tostring(lineV).."px")
table.insert(result,"; left:"..tostring((w - oWid )/2).."px; width: 0px")
table.insert(result,"; height: "..tostring(w -lineV).."px")
table.insert(result,"; border-right: "..shape.outlineWidth.." "..shape.outlineStyle.." "..getColor(shape.outlineColor))
table.insert(result,"; background:transparent; color:inherit;\"></div>")
if shape.shape=='rulea' then
table.insert(result,"<div style=\"display:inline-block; position: absolute; top: 0px") --and add arrow head
table.insert(result,"; left:"..tostring(w/2-( oWid/2)-oWid*0.55-2).."px; width: 0; height: 0; outline-width: 0px")
table.insert(result,"; border-left: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-right: "..tostring(oWid*1.1+2).."px solid transparent")
table.insert(result,"; border-bottom: "..tostring(oWid*3+16).."px solid "..getColor(shape.outlineColor).."\"></div>")
end
table.insert(result,"</div>")
end
local function makeCurveA(result,row,shape) -- draw a curve with Arrow -----
local w,h=getsize(shape.shapeSize) -- = width,height
local oWid=tonumber(string.match(shape.outlineWidth,"[%.%d]+"))
local Angle=tonumber(string.match(shape.shapeAngle,"[%.%d]+"))
table.insert(result,'<div style="position: absolute;') --set up out div, which will allow the whole to rotate
table.insert(result,'top:'..tostring(row.gridy - (w + oWid*3+16)/2)..'px;')
table.insert(result,'left:'..tostring(row.gridx - ( w + oWid*3+16)/2)..'px; ')
table.insert(result,'width: '..tostring(w+oWid*3+16)..'px; ')
table.insert(result,'height: '..tostring(w+oWid*3+16)..'px; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate( '..tostring(Angle-120)..'deg);">')
else table.insert(result,'transform: rotate( '..tostring(Angle -62)..'deg);">')
end
table.insert(result,'<div style="position: absolute;') --set up div for the rounded corner of a rectangle
table.insert(result,'border-left: '..shape.outlineWidth..' '..shape.outlineStyle..' '..getColor(shape.outlineColor)..';')
if shape.shape=='curvea' then
table.insert(result,'border-radius: 10000px 0 0 '..tostring(w)..'px; top:0px; left:'..tostring(w*0.25)..'px;')
else
table.insert(result,'border-radius: '..tostring(w)..'px 0 0 10000px;')
table.insert(result,'top:'..tostring((oWid*3+16)/2+w*0.15)..'px; left:'..tostring(w*0.25)..'px;')
end -- and add a triangular arrow head
table.insert(result,'width: '..tostring(w)..'px; height: '..tostring(w)..'px;"></div><div style="position: absolute; ')
if shape.shape=='curvea' then
table.insert(result,'transform: rotate(180deg); top:'..tostring(w-1)..'px; ')
else table.insert(result,'transform: rotate(0deg); top: '..tostring(0-( ( oWid*3+16)/2)+1+( w*0.15) )..'px;')
end -- reverse
table.insert(result,'left:'..tostring(0-( oWid*0.6)-2+(w*0.25))..'px;')
table.insert(result,'width: 0; height: 0; outline-width: 0px; border-left: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-right: '..tostring(oWid*1.1+2)..'px solid transparent;')
table.insert(result,'border-bottom: '..tostring(oWid*3+16)..'px solid '..getColor(shape.outlineColor)..';"></div></div>')
end
local function makeLineTo (result,x1,y1,x2,y2,oWid, oStyle, oCol,double)
table.insert(result,"<div style=\"display:inline-block; position: absolute;")
-- draw a line between x1,y1 and x2,y2, px-coords where 0,0 is centre of frame
-- Maths calculations thanks to ES
table.insert(result,"left: "..tostring(x1+( (x2-x1)/2) - (math.sqrt( ( x2-x1)^2 + (y2-y1)^2 )/2)-1).."px;")
table.insert(result,"top: "..tostring(y1+( ( y2-y1 )/2) ).."px;")
table.insert(result,"width: "..tostring(math.sqrt( (x2-x1 )^2 + ( y2-y1 )^2) ).."px;")
table.insert(result,"height: "..tostring(double).."px; background-color:transparent; color:inherit; ")
table.insert(result,"outline-width: 0; border-bottom: "..oWid.." "..oStyle.." "..getColor(oCol)..";" )
if double>1 then table.insert(result,"border-top: "..oWid.." "..oStyle.." "..getColor(oCol)..";" ) end
if x1==x2 then table.insert(result,"transform: rotate(90deg);")
else table.insert(result,"transform: rotate("..tostring(math.atan(( y2-y1)/( x2-x1 ) )*180/math.pi).."deg);\"></div>")
end
end
local function makeClipPath(result,row,shape,outline,tshape) --tshape is a flag to show if the tooltip (title=) is wanted
-- return the text css div code to position and draw a shape occupying a specified clippath
if not pathshape[shape.shape] then
debugmsg(mw.addWarning('shape'..row.code..' = '..shape.shape..'. The shape name is not defined'))
return
end
local w,h,r=getsize(shape.shapeSize)
if string.match(shape.shape,"circle") or string.match(shape.shape,"square") then h=w end -- use ellipse and box for stretched shapes
local shifth,shiftv = 0,0
if string.match(shape.shape,"triangle") then
shiftv,shifth =tshift(shape.shapeAngle)
end
if outline then
local p=tonumber(string.match(shape.outlineWidth,"[%.%d]+")) or 0
w=w+p*2
h=h+p*2
end
table.insert(result,"<div ")
if tshape then -- Add tooltip if needed
table.insert(result," title=\""..row.title.."\" ")
end
table.insert(result,"style=\"display:inline-block; position: absolute; background-color:")
if outline then
table.insert(result,getColor(shape.outlineColor)) -- fill with outline colour, to make a 'base layer'
else
table.insert(result,getColor(shape.shapeColor))
end
table.insert(result,"; color:inherit; clip-path:path(nonzero, '"..pathshape[shape.shape].."') ")
-- adds the required clippath data from the table of pathshape string literals
table.insert(result,"; top:"..tostring(row.gridy - 10 + h*shiftv).."px")
table.insert(result,"; left:"..tostring(row.gridx - 10 + w*shifth).."px")
table.insert(result,"; width:20px") -- needs to be a path within a 20px20px box, and then rescales using size values to match other shape sizes
table.insert(result,"; height:20px; transform:scale("..tostring(w/16)..", "..tostring(h/16)..")")
if shape.shapeAngle ~= "0deg" then
table.insert(result," rotate("..shape.shapeAngle..")")
end
table.insert(result,"\"></div>")
end
local function makeImage(result,row,shape)
local w,h,r=getsize(shape.shapeSize)
local image=shape.shapeFile
if not image or image=='' then image='Red pog.svg' end
local imagediv=mw.html.create ('div')
imagediv:css('position', "absolute")
if shape.shapeAngle ~= "0deg" then
imagediv:css('transform',"rotate("..shape.shapeAngle..")")
end
imagediv
:css('top', (row.gridy-1 + math.min(h/2-12,0) - h/2).."px") --File seems to adjust pos for small images
:css('left', (row.gridx-1-w/2).."px")
:css('background-color', "transparent" )
:css('color','inherit')
:wikitext('[[file:'..image..'|'..tostring(w+2)..'px|alt='..(row.title or '')..'|link=]]')
table.insert(result,tostring(imagediv))
end
local function makePhotoPanel(result,row,shape)
local h=row.ppheight
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;' )
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
table.insert(result, 'width: '..tostring(row.ppwidth)..'px; height: '..tostring(h)..'px; border-radius: 2px; color:inherit;')
table.insert(result, 'background-color: #E8E8D6; outline: 2px solid '..getColor(shape.textCL)..'; box-shadow: 2px 2px 4px #33203335;"></div>')
if row.photoImage and row.photowidth >0 then
table.insert(result, '<div style="position: absolute; top: '..tostring(row.gridy+row.dy-h/2)..'px;')
if row.labelPos=='left' or string.find(row.labelPos,'west') then
--debugmsg(row.labelText..', Align=right, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx - row.ppwidth/2)..'px;')
row.dx=row.dx+row.photowidth/2
else
--debugmsg(row.labelText..', Align=left, ppwidth='..tostring(row.ppwidth)..', photowidth='..row.photowidth..', dx='..tostring(row.dx)..', dy='..tostring(row.dy))
table.insert(result, 'left: '..tostring(row.gridx+row.dx + (row.ppwidth-row.photowidth) - row.ppwidth/2)..'px;')
row.dx=row.dx-row.photowidth/2
end
table.insert(result, 'background-color:transparent; color:inherit; border-radius: 2px;">')
table.insert(result,'[[File:'..row.photoImage..'|x'..tostring(h)..'px|File:'..row.photoImage..']]</div>')
row.labelPos='center'
end
end
local function makePanelText(result, row, shape)
local w,h,r=getsize(shape.shapeSize)
local ty=tonumber(string.match(shape.textSZ or '11',"%d+") )
table.insert(result,'<div style="position:absolute; line-height: 120%; font-size: '..shape.textSZ..'; color:'..getColor(shape.textCL)..';')
if row.labelPos == 'left' or string.find(row.labelPos,'west') then
table.insert(result,'top: '..tostring(row.dy+row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+3)..'px; text-align: left; width:'..tostring(w)..'px;')
elseif row.labelPos == 'right' or string.find(row.labelPos,'east') then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+w-3)..'px; text-align: right; width: max-content; transform: translateX(-100%);')
elseif row.labelPos == 'top' or row.labelPos== 'north' then
table.insert(result,'top: '..tostring(row.gridy+(ty/3))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
elseif row.labelPos == 'bottom' or row.labelPos=='south' then
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*1.1
table.insert(result,'top: '..tostring(row.gridy+w-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
else -- center or centre
local bry=(select(2, string.gsub(row.labelText,"<br>", ""))+1)*0.6
table.insert(result,'top: '..tostring(row.gridy+(h/2)-(ty*bry))..'px;')
table.insert(result,'left: '..tostring(row.gridx+(w/2))..'px; text-align: center; width:max-content; transform: translateX(-50%);')
end
if shape.textSP and shape.textSP ~='0px' then table.insert(result,"letter-spacing: "..shape.textSP..';') end
if shape.textLH and shape.textLH ~='120%' then table.insert(result,"line-height: "..shape.textLH..';') end
table.insert(result,"vertical-align: middle;\">"..row.labelText.."</div>")
end
local function makeTextItem(result, row, shape, align, tlink, textItem, dotItem)
local w,h,r=getsize(shape.shapeSize)
table.insert(result,"<div ")
if row.title ~= "" then table.insert(result," title=\""..row.title.."\" ") end
local ty,bry,linkoffset = 0,0,0
local compy=0
local lh=tonumber(string.match(shape.textLH or '120',"%d+"))/100
if dotItem==1 or (dotItem==2 and row.posType~='n-line') then -- if there is a dotTag in the middle of the shape then use the tag settings
ty=tonumber(string.match(shape.tagSize,"%d+")) or 0
table.insert(result,"style=\"position:absolute; line-height: 120%; top: "..tostring(row.gridy-ty*0.62))
table.insert(result,"px; left: "..tostring(row.gridx).."px; width: fit-content; ")
table.insert(result,"text-align: center; color: "..getColor(shape.tagColor).."; background-color: transparent;")
local trf=""
if shape.tagAngle ~="0deg" then trf=" rotate("..shape.tagAngle..")" end
table.insert(result, "transform: translateX(-50%)"..trf.."; font-size: "..shape.tagSize..";")
if shape.tagSpacer~='0px' then table.insert(result, "letter-spacing:"..shape.tagSpacer..";") end
else -- or add tfx settings for left, right or center align, colors, backgrounds, border-outline
table.insert(result,'style="position:absolute; ')
if dotItem==2 then -- dotTag is out at x,y so 85%
ty=tonumber(string.match(shape.tagSize or '0',"%d+"))
table.insert(result,'font-size: '..shape.tagSize..'; padding:0px 2px;line-height: 85%; top: '..tostring(row.dy+row.gridy-ty*0.52))
else -- it is labelText, so use textLH or 120%
ty=tonumber(string.match(shape.textSZ or '11',"%d+"))
if row.labelPos=='northwest' or row.labelPos=='northeast' then compy=-ty
elseif row.labelPos=='southeast' or row.labelPos=='southwest' then compy=ty/2 end
if row.labelPos and not (row.labelPos== 'auto' or row.labelPos=='') then
bry=(select(2, string.gsub(row.labelText,"<br>", ""))*lh) -- is it a multiline text? expand by line-height /120%?
if row.posType == 'with-line' then bry=0 end
if row.labelPos=='bottom' or row.labelPos == 'south' then bry = 0 -- and shift by none, all or half
elseif row.labelPos=='top' or row.labelPos == 'north' then
if shape.shape=='image:' then bry= 1 + math.min(w/2-10,0)-bry*ty
else bry= -bry*ty+2
end
else bry=-bry*(ty/2 * lh)
end
end
if row.posType == 'photo-panel' then bry=bry+3 end
table.insert(result,'font-size: '..shape.textSZ..'; padding:0px 3px;line-height: '..shape.textLH..'; ')
table.insert(result,'top: '..tostring(row.dy+row.gridy+compy+bry-ty*lh/2))
end
table.insert(result,"px; left: "..tostring(row.dx+row.gridx).."px; color: "..getColor(shape.textCL).."; ")
table.insert(result,"width: max-content; ")
local trf=""
if shape.textNG ~="0deg" then trf="rotate("..shape.textNG..")" end
if shape.textOL~="0px" then
table.insert(result,"background-color: "..getColor(shape.textBG).."; ")
table.insert(result,"border: "..shape.textOL.." solid "..getColor(shape.textCL).."; border-radius:6px;")
else table.insert(result,"background-color: transparent;")
end
if row.labelPos=="right" or string.find(row.labelPos,'east') then
table.insert(result,"text-align: left; ")
linkoffset=w
if shape.textNG ~="0px" then table.insert(result,"transform-origin: left; transform: rotate("..shape.textNG.."); ") end
elseif row.labelPos=="left" or string.find(row.labelPos,'west') then
if shape.textNG ~="0px" then table.insert(result,"transform-origin: right;") end
linkoffset=-w
table.insert(result,"text-align: right; transform: translateX(-100%) "..trf.."; ")
else
table.insert(result,"text-align: center; transform: translateX(-50%) "..trf.."; ")
end
table.insert(result,'font-weight: normal; line-height: '..shape.textLH..'; letter-spacing:'..shape.textSP..'; vertical-align: bottom;')
end
if string.find(shape.textAT or '','bold') then textItem='<b>'..textItem..'</b>' end
if string.find(shape.textAT or '','italic') then textItem='<i>'..textItem..'</i>' end
if shape.textOL=='0px' and shape.textBG~='transparent' and dotItem==0 then
table.insert(result,'\"><span style=\"background-color: '..getColor(shape.textBG)..'; color:inherit;\">'..textItem..'</span></div>')
else
table.insert(result,"\">"..textItem.."</div>")
end
if tlink~='' and not string.match(row.param1 or "","nolink") then
table.insert(result,makeLinkBox(row.gridx+row.dx+linkoffset, row.gridy+row.dy+bry, 16, row.dotTag..' '..row.title,tlink))
end
end
local function getshapetable(row,shape) -- Construct CSS divs for a dot from shape and map data
local result={}
local w,h,r=getsize(shape.shapeSize)
local tshape,tlink=checkfortooltip(row.title,row.dx,row.dy,row.dotTag,row.dlink,string.match(row.param1 or "","nolink") )
local align=row.labelPos or ''
local offsetx,offsety=0,0
local ty=tonumber(string.match((shape.tagSize or 9),"%d+" ))
if row.labelText and row.labelText~='' then ty=tonumber(string.match((shape.textSZ or 11),"%d+" ))
else align='center' end -- it is just for dotTag, so justify center
-- identify align value and extend offsets
local widthzone,heightzone = w/2+4,h/2+4
local theta,r = math.deg( math.atan2(row.dy, row.dx)) , math.sqrt(row.dx^2 + row.dy^2)
if align=='auto' or align=='' then
if (theta < -112 or theta > 112) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "left" offsetx=1
elseif (theta > -68 and theta < 68) and math.abs(row.dx)>=w/2 and math.abs(row.dy)<w/1.4 then align = "right" offsetx=-1
elseif theta <0 then offsety=ty/2-1 -- bottom
else offsety=0-ty/2+1 -- top
end
elseif align=='left' or string.find(align,'west') then
row.dx=row.dx - w/2 widthzone=4 offsetx=-1
elseif align=='right' or string.find(align,'east') then
row.dx=row.dx + w/2 widthzone=4 offsetx=1 offsety=-ty/2 +1
elseif align=='top' or align=='north' then
if string.find(shape.shape,'curve') then
row.dy=row.dy-(h/2)-12 heightzone=4 offsety=ty/2-1
else
row.dy=row.dy - h-2 heightzone=4 offsety=ty/2-1
end
elseif align=='bottom' or align=='south' then
if string.find(shape.shape,'curve') then
row.dy=row.dy+(h/2) heightzone=4 offsety=ty/2-1
else
row.dy=row.dy + h+2 heightzone=4 offsety=0-ty/2-1
end
end
if r > widthzone and shape.textEW ~= "0px" and not string.match(row.param1 or "","noline") then
makeLineTo(result, row.gridx+1, row.gridy-1, row.gridx+row.dx+offsetx, row.gridy+row.dy+offsety, shape.textEW, shape.textES,shape.textEC,1)
end
if row.posType and row.posType=='mark-line' then
if row.gridx2 and row.gridy2 then
makeLineTo(result, row.gridx, row.gridy, row.gridx2, row.gridy2, tostring(row.mlWidth)..'px', row.mlStyle, getColor(shape.textEC),row.mlGap)
-- debugmsg('makeLineTo line drawn from '..row.code..' with width '..tostring(row.mlWidth)..'px '..row.mlStyle..' and color '..getColor(shape.textEC))
end
end
if w ~= 0 then
if shape.shape=='itriangle' then shape.shape='triangle' shape.shapeSize=tostring(w)..'px'..tostring(w/2)..'px' end
if shape.shape=="triangle" then
if shape.outlineWidth ~= "0px" then
makeTriangle(result,row,shape,true,false) -- larger triangle to give the outline, if required
end
makeTriangle(result,row,shape,false,tshape) -- smaller triangle to fit over the top
elseif shape.shape=="square" or shape.shape=="box" or shape.shape=='panel' then
makeSquare(result,row,shape,tshape)
elseif shape.shape=="circle" or shape.shape=="ellipse" then
makeCircle(result,row,shape,tshape)
elseif string.find(shape.shape,'image:')==1 then
makeImage(result,row,shape)
elseif shape.shape=="rulea" or shape.shape=='rule' then
makeRuleA(result,row,shape)
elseif shape.shape=="curvea" or shape.shape=="curvec" then
makeCurveA(result,row,shape)
else -- use a pathshape clipPath
if shape.outlineWidth ~= "0px" then
makeClipPath(result,row,shape,true,false) -- larger path-shape to give the outline, if required
end
makeClipPath(result,row,shape,false,tshape)
end
end
if row.ppwidth and row.ppwidth>0 then makePhotoPanel(result,row,shape) end
if shape.shape=='panel' and row.labelText then makePanelText(result, row, shape)
else
if row.dotTag and row.dotTag ~= "" then -- there is a dotTag
if (row.dx==0 and row.dy==0) and w>0 then -- it is on the dot so if dotsize is not 0 any label is ignored
makeTextItem(result, row, shape, align, tlink, '<b>'..row.dotTag..'</b>', 1)
else
if row.labelText and row.labelText~='' then -- tag and label both used
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
else
makeTextItem(result, row, shape, align, '', '<b>'..row.dotTag..'</b>', 1)
makeTextItem(result, row, shape, align, tlink, row.dotTag, 2) -- tag is ouside the dot
end
end
else
if (row.labelText and row.labelText~='') then -- just the label. No tag
makeTextItem(result, row, shape, align, tlink, row.labelText, 0)
end
end
end
if tlink and tlink~='' then
table.insert(result,makeLinkBox(row.gridx, row.gridy,w+3,row.dotTag..' '..row.title,tlink))
end
return table.concat(result)
end
local function getmapframecontent(args,use)
local result, comma= {}, ''
local propertyTable= {}
local color=splitItem(string.lower( args['map-data-color'] or 'dark orange'), 2)
if use == 'basemap' then
table.insert(result, '[')
propertyTable.stroke = "#000000"
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '15')/100
else
propertyTable.title = args['map-data-text'] or args['map-data'] or ''
propertyTable.stroke = getColor(color[1])
propertyTable['stroke-width'] = tonumber(args['map-data-width'] or '6')
propertyTable['stroke-opacity'] = tonumber(color[2] or '100')/100
end
if args['map-data-inverse'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geomask',
ids = args['map-data-inverse'],
properties = {title = (args['map-data-text'] or ''),
stroke = '#555555',
fill = '#555555',
['fill-opacity'] = 0.1,
['stroke-width'] = 1,
['stroke-opacity'] = 0.5 }
}
table.insert(result, mapJson)
comma=', '
end
if args['map-data-heavy'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-heavy'],
properties = { title = args['map-data-heavy'],
stroke = propertyTable.stroke,
['stroke-width'] = 9,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data-light'] then
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data-light'],
properties = { title = args['map-data-light'],
stroke = propertyTable.stroke,
['stroke-width'] = 3,
['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.25 }
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-data'] then
propertyTable['stroke-opacity'] = propertyTable['stroke-opacity'] or 0.1
local mapJson = mw.text.jsonEncode{
type = 'ExternalData',
service = 'geoline',
ids = args['map-data'],
properties = propertyTable
}
table.insert(result, comma .. mapJson)
comma=', '
end
if args['map-wdqs'] then -- inserts a SPARQL wdqs query to find geopoints, geoshapes etc from wikidata
local mapSparql = mw.text.jsonEncode{
type = 'ExternalData',
service = args['map-wdqs-type'] or 'geopoint',
query = args['map-wdqs'],
}
table.insert(result, comma .. mapSparql)
-- debugmsg('mapSparql = ' .. mapSparql)
end
if args['map-raw'] then
table.insert(result, comma .. args['map-raw'])
end
if use=='basemap' then table.insert(result,']') end
return table.concat(result)
end
--eg | minilocator=filename,bottom right,132px153px, 38%,60%, 22px
local function makeLocatorMap (args, result)
local miniFile,pos,itemlist,miniW,miniH, miniX,miniY,miniBox, miniBH
if args['mini-locator'] then
pos,miniFile=extractItem(args['mini-locator']) -- first item is filename, in quotes if it includes commas
itemlist=splitItem(pos,5) -- put items in a table filename removed, position,WpxHpx, x%y%,box
pos=itemlist[2] or 'right'
miniW,miniH=getsize(itemlist[3])
miniX=tonumber(string.match(itemlist[4] or '0','[%d]+'))*miniW/100
miniY=tonumber(string.match(itemlist[5] or '0','[%d]+'))*miniH/100
miniBox=tonumber(string.match(itemlist[6] or '0','[%d]+'))
miniBox=miniBox*miniW/100
miniBH=miniBox * maplist.height/maplist.width
elseif args['mini-file'] then
miniFile = args['mini-file']
pos=string.lower(args.minimap or 'right')
if pos=='file' then pos='right' end
miniW,miniH = tonumber(args['mini-width'] or 60), tonumber(args['mini-height'] or 60) -- find top left corner of locator
miniBox,miniBH=tonumber(args['minimap-boxwidth'] or '0'),0 -- firm up pos offsets for dot (with % or not) and boxsize if any,
miniX, miniY=args['minipog-gx'],args['minipog-gy']
if not miniX then miniX=tonumber(args['minipog-x'] or '0') else miniX=tonumber(miniX)*tonumber(miniW)/100 end
if not miniY then miniY=tonumber(args['minipog-y'] or '0') else miniY=tonumber(miniY)*tonumber(miniH)/100 end
if args['minipog-gx'] then miniBox=miniBox*miniW/100 end
miniBH=miniBox * maplist.height/maplist.width
else return end
local miniTop,miniLeft=0,1
if not string.find(pos,'top') then --only use top left, as link box is in top right or put bottom left or right
miniTop=maplist.height+2-miniH
if string.find(pos,'right') then
miniTop=miniTop-15 -- to avoid (c) line
miniLeft=maplist.width-1-miniW
end
end
table.insert(result,'<div style="position: absolute; outline-width: 1px; outline-style: solid; outline-color: white;')
table.insert(result,'top: '..tostring(miniTop)..'px; left:'..tostring(miniLeft)..'px; width:'..tostring(miniW)..'px;' )
table.insert(result,'background-color:transparent; color:inherit;">[[File:'..miniFile..'|'..tostring(miniW)..'px|File:'..miniFile..']]</div>')
if miniX and miniX>0 then
if args['minipog-y'] then miniY=miniY/1.04 end
if args['minipog-x'] then miniX=miniX/1.04 end
if miniBox<1 then
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-3-6)..'px; left:'..tostring(miniX+miniLeft-3)..'px;')
table.insert(result, 'width: 6px; background-color:transparent; color:inherit;">[[File:Red pog.svg|6px|link=]]</div>')
else
table.insert(result,'<div style="position: absolute; top: '..tostring(miniY+miniTop-miniBH/2)..'px; left:'..tostring(miniX+miniLeft-miniBox/2)..'px;')
table.insert(result, 'width: '..tostring(miniBox)..'px; height:'..tostring(miniBH)..'px; outline:1px solid #AA1205; background-color:#AA120522; color:inherit;"></div>')
end
end
end
local function makeArcText(args,result,nval)
local items, itemlist='',{}
local arcText=''
if args['arc'..nval] then
items=convertCoords (args['arc'..nval])
items,arcText=extractItem(items) -- first item is text, in quotes if it includes commas
itemlist=splitItem(items,9) -- put items in a table: text, lat,lon,size,color,angle,gap,radius,ellipse
if itemlist[4]=='' then itemlist[4]='12' end
itemlist[4]=string.match((itemlist[4] or '12'),"[%.%-?%d]+")
if itemlist[5]=='' then itemlist[5]='grey' end
itemlist[6]=string.match((itemlist[6] or '0'),"[%.%-?%d]+")
itemlist[7]=string.match((itemlist[7] or '0'),"[%.%-?%d]+")
itemlist[8]=string.match((itemlist[8] or '0'),"[%.%-?%d]+")
end
if args['arc-coord'..nval] then
local itemTab=splitItem(convertCoordsTrad (args['arc-coord'..nval]),2)
maplist.lat=tonumber(string.match(itemTab[1],"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(itemTab[2],"[%.%-?%d]+"))
else
maplist.lat=tonumber(string.match(args['arc-lat'..nval] or itemlist[2] or '0',"[%.%-?%d]+"))
maplist.lon=tonumber(string.match(args['arc-lon'..nval] or itemlist[3] or '0',"[%.%-?%d]+"))
end
local arcX,arcY=maptogrid(maplist,6)
arcText = args['arc-text'..nval] or arcText
local fontSize =tonumber(args['arc-text-size'..nval] or itemlist[4] or '12')
local textColor=getColor(string.gsub(args['arc-text-color'..nval] or itemlist[5] or 'grey','[%s]+','') )
local arcAngle= tonumber((args['arc-angle'..nval]) or (itemlist[6]) or '45')-90
local arcRadius =tonumber(args['arc-radius'..nval] or itemlist[8] or '0.05')
local arcGap = tonumber(args['arc-gap'..nval] or itemlist[7] or '1')* ( ( math.sin(8-math.rad(arcRadius))^8 )+0.4 )*( ( fontSize+6 )/15 )
arcRadius=450*arcRadius*0.75
local ellipseFactor=tonumber(args['ellipse-factor'..nval] or itemlist[9] or '1')
local arcRotate =arcAngle+90
if arcGap<0 then arcRotate=arcAngle-90 end
local latF=arcY - fontSize + (0-(math.sin(math.rad(arcAngle))) * arcRadius)
local lonF=arcX - fontSize +(0-(math.cos(math.rad(arcAngle))) * arcRadius)
local step=1
for codepoint in mw.ustring.gcodepoint( arcText ) do -- block step=1,#arcText do
table.insert(result,'<div style="position: absolute;')
local posY=tostring(round( (latF + (math.sin(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius)) *ellipseFactor,2))..'px;'
local posX=tostring(round( (lonF + (math.cos(math.rad(arcAngle+((step-1)*arcGap))) * arcRadius))/ellipseFactor,2))..'px;'
table.insert(result,' top: '..posY..' left: '..posX..' transform: rotate( '..tostring(round(arcRotate +((step-1)*arcGap)),2)..'deg);')
table.insert(result,'width:'..tostring(fontSize*2)..'px; text-align: center; background-color:transparent; color: '..textColor..';')
table.insert(result,'vertical-align: baseline; font-size: '..tostring(fontSize)..'px;">'..mw.ustring.char(codepoint)..'</div>')
step=step+1
end
end
local function makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
local item={}
itemdescription=stripdivs(itemdescription or '')
local templon=lon
if lon > 180 then templon=lon-360 end --for hemisphere+ or -1 dots
if lon < -180 then templon=lon+360 end -- use 'real' coordinates for geohack label, while retaining shifted coords for plot
if itemcolor=='transparent' then itemcolor='white' end
itemcolor=getColor(itemcolor) -- ensure no opacity, which breaks maplink
if string.find(itemcolor,'#')==1 and #itemcolor>7 then itemcolor=string.sub(itemcolor,1,7) end
table.insert(item, '{ "type": "Feature", "properties": {')
table.insert(item, ' "title": "'..itemtitle..'",')
table.insert(item, ' "description": "'..itemdescription)
table.insert(item, ' ([https://geohack.toolforge.org/geohack.php?params='..tostring(lat)..';'..tostring(templon))
table.insert(item, '_dim:2000 '..tostring(lat)..','..tostring(templon)..'])",')
table.insert(item, ' "marker-symbol": "-number-'..string.gsub(group,'%W','')..'", "marker-size": "medium", "marker-color": "'..itemcolor..'" },')
table.insert(item, ' "geometry": {"type": "Point", "coordinates": ['..tostring(lon)..','..tostring(lat)..'] } }')
return table.concat(item)
end
local function makeLegendBox(result,args)
local legend ={}
local line,count, maxWidth='',1,8
local item
line,item=extractItem(args.legendBox or '')
local a='' for c in item:gmatch('.') do a=a..(c:gsub('%^','<br>') or c) end
legend.Text = a -- convert hats to line breaks
line=splitItem(line,6) -- (text, size, poition,background color, text/outline color, param options)
legend.Size=line[2] or '150px80px1px'
legend.Pos=line[3] or '10px10px'
legend.Background=line[4] or 'beigeground'
legend.Color=line[5] or 'darkbrown'
legend.Param= line[6] or ''
local argnum,legendCount,titleHeight='1',1,0
if (legend.Text and legend.Text~='') then
titleHeight=15+(13.4*(select(2, string.gsub(legend.Text,"<br>", ""))))
end
local legendLine,legendGroup,legendY={},{},{}
while args['legendItem'..argnum] do -- assign legendLine, legendGroup, legendY for each dot
line,legendLine[legendCount] = extractItem(args['legendItem'..argnum] or '')
line=splitItem(line,3)
legendGroup[legendCount]=line[2] or argnum
if shapeList[legendGroup[legendCount]] then
maxWidth=math.max(tonumber(string.match(shapeList[legendGroup[legendCount]].shapeSize or '10','[%d]+')),maxWidth)
else maxWidth=math.max(tonumber(string.match(shapeList['1'].shapeSize or '10','[%d]+')),maxWidth)
end
maxWidth=maxWidth+1
if line[3] then
legendY[legendCount]=tonumber(string.match(line[3],'[%d]+'))
else legendY[legendCount] = 3+maxWidth*(legendCount-1)+titleHeight
-- if (legend.Text and legend.Text~='') then legendY[legendCount]=legendY[legendCount]+15 end
end
legendCount=legendCount+1
argnum=tostring(legendCount)
end
local w,h,r=getsize(legend.Size or '')
local x,y=getsize(legend.Pos or '')
local div=mw.html.create ('div')
div:css('position', 'absolute')
div:css('outline', '1px solid'..getColor(legend.Color))
if r~=0 then div:css('border-radius',tostring(r).."px") end
div
:css('top', y.."px")
:css('left', x.."px")
:css('width', w.."px")
:css('height', h.."px")
:css('line-height','105%')
:css('background-color', getColor(legend.Background) )
:css('color','inherit')
if not string.find(legend.Param,'noshadow') then div:css('box-shadow', '2px 2px 4px #33203335') end
div:tag( 'div' )
:css('position', 'absolute')
:css('top','1px')
:css('left', (w/2).."px")
:css('width',(w-8)..'px')
:css('text-align', 'center')
:css('color', getColor(legend.Color))
:css('transform', 'translateX(-50%)')
:css('font-size','11px')
:wikitext(legend.Text)
:done()
for lct=1,legendCount-1 do
--local t=legendGroup[lct]
local shape=shapeList[legendGroup[lct]] or shapeList['1']
local row={}
row.gridx=3+maxWidth/2
row.gridy=(legendY[lct] or 0) + 5
if shape.shape=='image:' then row.gridy=row.gridy+6 end
row.dx=0 row.dy=0
local legendShape= getshapetable(row,shape)
div:wikitext(legendShape)
:tag( 'div' )
:css('position', 'absolute')
:css('top',(legendY[lct] or 0)..'px')
:css('left', (maxWidth+6)..'px')
:css('width', (w-maxWidth-6)..'px')
:css('text-align', 'left')
:css('line-height','103%')
:css('color', getColor(legend.Color))
:css('font-size','10px')
:wikitext(legendLine[lct])
:done()
end
div:allDone()
table.insert(result,tostring(div))
end
function p._main ( args )
local result={}
local frame=mw.getCurrentFrame()
local dotTable={}
local magVal,scaleVal = args.magnify,''
local origH,origW=maplist.height,maplist.width
if magVal then --set up the values needed to magnify the top-right portion of the map
magVal=tonumber(string.match(magVal or '0',"[%.%-?%d]+")) or 1
if magVal>1 and magVal <=2 then
maplist.height= round(maplist.height/magVal,0)
maplist.width=round(maplist.width/magVal,0)
scaleVal='transform: scale('..magVal..') translateY('..tostring((origH-maplist.height)/2)..'px);'
else magVal=1
end
--debugmsg('magVal='..magVal..', Magnify: H='..maplist.height..', ('..origH..'), W='..maplist.width..', ('..origW..'), scale='..scaleVal)
--debugmsg('top: '..tostring(((origW-maplist.width))/2)..'px;right: '..tostring(0-((origW-maplist.width))/2)..'px; (alt top: '..tostring(((origH-maplist.height))/2)..'px;)')
end
-- set up the three nested div boxes (plus an extra if centered) to put the map plus title/caption area, in an appropriate frame on the page
if args.float=='center' or args.float=='centre' then table.insert(result,'<div class="center"><div class="thumb tnone">')
elseif args.float=='left' then table.insert(result,'<div class="thumb tleft">')
else table.insert(result,'<div class="thumb tright">')
end
table.insert(result,'<div class="thumbinner" style="position: relative; top: 0px; right: 0px; width: '..(args.width or "400")..'px;">')
if args.title then table.insert(result,'<div class="center" style="font-weight:bold">'..args.title..'</div>') end
if magVal and magVal >1 and magVal<=2 then
table.insert(result,'<div class="thumbinner noresize" style="display:block; position: relative; outline:0px; border:0px; padding:0px; background-color:transparent; color:inherit;')
table.insert(result,'top: 50%; right: '..tostring(0-((origW-maplist.width))/2)..'px; ')
table.insert(result,'height: '..origH..'px; width: '..maplist.width..'px; '..scaleVal..'">')
else
magVal=1
table.insert(result,'<div class="thumbinner noresize" style="position: relative; outline:0px; border:0px; padding:0px;')
table.insert(result,'top: 0px; right: 0px; ')
table.insert(result,'height: '..maplist.height..'px; width: '..maplist.width..'px ">')
end
-- Create the basemap using mapframe
local mapframecontent=getmapframecontent(args,'basemap')
table.insert(result, frame:extensionTag{ name ='mapframe', content=mapframecontent, args={width=tostring(maplist.width), height=tostring(maplist.height),
zoom=tostring(maplist.zoom), longitude=tostring(maplist.lonbase), latitude=tostring(maplist.latbase), mapstyle=maplist.mapstyle, frameless=true } } )
--Add coverall box to block the unhelpful links from mapframe - which wouldn't include all the dots. Reinstate some links to osm and wikimedia
table.insert(result,'<div style="position: absolute;width:'..tostring(maplist.width)..'px; height:'..tostring(maplist.height)..'px;')
table.insert(result,'top:0px;left:0px;background-color:#FFFFFF00; color:inherit;"></div>')
--Add replacent hover-links for OpenStreetMap and maps terms and conditions
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-13)..'px; width: 12px; height: 12px">')
table.insert(result,'[[file:Transparent.svg|12px|link=https://www.openstreetmap.org/copyright|'..aboutOSM..']]</div>')
table.insert(result,'<div style="position: absolute; top: '..tostring(maplist.height-18)..'px; left: '..tostring(maplist.width-110)..'px;')
table.insert(result,'width: 12px; height: 12px background-color: transparent; color:inherit;">')
table.insert(result,'[[file:Transparent.svg|12px| link=https://foundation.wikimedia.org/wiki/Policy:Maps_Terms_of_Use|'..termsOfUse..']]</div>')
-- Add scale-line
if not args.scalemark or args.scalemark~='0' then
local top=maplist.height-42
local left=maplist.width-61-(tonumber(args.scalemark or '1'))
local minipos=string.lower(args.minimap or '') -- scalemark gets pushed left if it would be behind the minimap
if minipos~='' and not(string.find(minipos,'left') or string.find(minipos,'top') ) then
local offset=tonumber(args.scalemark or '1')-tonumber(args['mini-width'] or '60')
if offset<1 then left=maplist.width-61-tonumber(args['mini-width'] or '60') end
end
if maplist.width-left >216 then top=top+14 end -- shunt scaleline down if it is beyond the copyright stuff
local scalek,scalem=getScale(maplist.zoom, maplist.latbase, magVal)
local magReduce=''
table.insert(result,"<div style=\"display:inline-block; position: absolute; background-color: #111111")
table.insert(result,"; color:inherit; clip-path:path(nonzero, 'M0,8 l0,4 l20,0 l0,-4 l-0.3,0 l0,3.7 l-19.4,0 l0,-3.7 z') ")
table.insert(result,"; width:20px") -- path is a 20px20px box, and then rescales
if magVal==1 then
table.insert(result,"; top:"..tostring(top-1).."px")
table.insert(result,"; left:"..tostring(left+16).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5)..", "..tostring(1.5)..")")
else -- shrink the scalemark to compensate for magnification
table.insert(result,"; top:"..tostring(top-(magVal*0.28)).."px")
table.insert(result,"; left:"..tostring(left+(16*(magVal*1.15))).."px")
table.insert(result,"; height:20px; transform:scale("..tostring(2.5*(1/magVal))..", "..tostring(1.5*(1/magVal))..")")
magReduce= 'scale('..tostring(1/magVal)..')'
end
table.insert(result,"\"></div>")
table.insert(result,'<div style="position: absolute; top: '..tostring(top)..'px; left: '..tostring(left+47)..'px; font-size: 9.5px; line-height: 126%; width: fit-content;')
table.insert(result,'color: #444433; background-color: transparent; text-align: right; transform: '..magReduce..' translateX(-100%);">'..scalek..'<br>'..scalem..'</div>')
end
--Set up the shapeList and dotList tables, to provide data to go on the map
local sgNumbers,sgSortable={},{} --s1,s2
sgNumbers["1"]="1"
if args.useFormatStyle and args.useFormatStyle=='shortstyle' then
shapeList=ParseShapeTypes (shapeList,args,"1")
for argindex,argv in pairs(args) do -- build a list of all the numbered sg's that have been used
if string.find(argindex,"sg[a-f,n%d]+") == 1 then -- only look through the sga,sgb, sgc,sgd,sge,sgf and sgn args
local x=string.match(argindex,"[%d]+") -- find its number
if x and not sgNumbers[x] then sgNumbers[x]=x end -- only add if not already found
end
if string.find(argindex,"sg[a-f]H") == 1 and highlightNum then highlightOption=true end
end
for indx,sgnum in pairs(sgNumbers) do table.insert(sgSortable,sgnum) end
table.sort(sgSortable,lessthan) -- put the list in a sortable form
for k,v in pairs(sgSortable) do -- work through the sorted list, parsing each set of sg's in turn, from 1 upwards
shapeList=ParseShapeTypes (shapeList,args,v)
end
if highlightOption==true then shapeList=ParseShapeTypes (shapeList,args,'H') end
end
local dotList,dotresult,dotItemTable,dotGroupList={},{},{},{}
if (not args.useFormatStyle) or args.useFormatStyle=='standardstyle' then
local dotmax=0
for indx in pairs(args) do
if string.match(indx,'mark%-coord[%d]+') or string.match(indx,'lat[%d]+') then
dotmax=math.max(dotmax, tonumber(string.match(indx,"[%d]+")))
end
if (indx=='shapeH') or (indx=='shape-colorH') or (indx=='shape-outlineH') or (indx=='label-colorH') then highlightOption=true end
end
dotItemTable=tradstyleParseShapes(args,dotItemTable,dotmax)
for argindex=1,dotmax do -- build a list of all the numbered coords or lat,lons that have been used
local x=tostring(argindex)
if args['mark-coord'..x] or (args['mark-lat'..x] and args['mark-lon'..x]) then
sgNumbers[x]=x -- add it to the list
end
end
else
for indx in pairs(args) do
if string.match(indx,'dot[%d]+') then table.insert(dotList,indx) end --add the index name for dot1, dot2 etc to dotList
end
table.sort(dotList,morethan)
for indx,dotName in pairs(dotList) do
dotresult=ParseData(args,string.match(dotName,'[%d]+') ) -- using each dot number, assign the settings for each dot to a dotresult item line
table.insert(dotItemTable,dotresult) -- and store that item line within the dotItemTable
end
end
for arcVal = 65,91 do -- check through args looking for any arcs
local arcLetter=string.char(arcVal)
if args['arc-text'..arcLetter] or args['arc'..arcLetter] then
makeArcText(args,result,arcLetter)
end
end
local dotdivs=''
local ddots=0
if dotItemTable[1] then
local ddots=(dotItemTable[1].lat or 0)+(dotItemTable[1].lon or 0)
end
local latbase = tonumber(maplist.latbase)
local lonbase = tonumber(maplist.lonbase)
if not latbase then latbase = maplist.lat or 0 end
if not lonbase then lonbase = maplist.lon or 0 end
local fgroup = 'F' .. tostring(latbase + lonbase + ddots)
local FullscreenList={}
local addcomma=''
for i,dotitem in pairs(dotItemTable) do -- working throug each dot item, merge the dot and shape values into a full set of css text
local dotgroup= dotitem.group or "0"
if dotitem.posType=='mark-line' and dotitem.markDest then --find destination xy values for any mark-lines
for n,v in pairs(dotItemTable) do
if v.code == dotitem.markDest then dotitem.gridx2=v.gridx dotitem.gridy2=v.gridy break end
end
end
local qtype=dotitem.group -- find which shape group each dot has been assigned
--debugmsg('dotgroup='..qtype..', sg='..(sgNumbers[qtype] or 'nil')..' , shapeList='..shapeList[qtype].shape)
if not sgNumbers[qtype] then qtype="0" end --shapeList[dotitem.group] will give access to the shape values for that dot
if highlightNum==dotitem.code and highlightOption==true and shapeList['H'] then
table.insert(result, getshapetable(dotitem,shapeList['H']))
else
table.insert(result, getshapetable(dotitem,shapeList[qtype])) -- Add the actual css instructions for each dot
end
if shapeList[dotgroup] and not string.find((dotitem.param1 or ''),'nomap') then -- only add if not excluded with 'nomap' labelText
local ftext=''
if dotitem.dotTag~='' and not string.match(dotitem.labelText or '','[%d]') then ftext=stripdivs(dotitem.dotTag or '')..' <br>' end
if (dotitem.labelText ~= ftext) and dotitem.dotLink =='' then ftext=ftext..' '..stripdivs(dotitem.labelText)..'<br>' end
if (dotitem.dotLink) and (dotitem.dotLink ~='') then ftext=ftext..dotitem.dotLink..'<br>' end
if dotitem.imageName then ftext=ftext..'[[File:'..dotitem.imageName..'|250px]]' end
table.insert(FullscreenList,1, makeFullscreenItem (string.gsub(ftext,"[\n]+"," "), dotitem.info,dotitem.lat,dotitem.lon,fgroup,shapeList[dotgroup].shapeColor)..addcomma )
addcomma=', '
end
-- makeFullscreenItem (itemtitle,itemdescription,lat,lon,group,itemcolor)
-- Always add to start of list, to reverse the sequence, and separate with commas except for first item, which is now at the end
end
if args.legendBox then makeLegendBox(result,args) end
if args.minimap or args['mini-locator'] then makeLocatorMap(args,result) end
-- add tag link and details for fullscreen version
addcomma=''
if (mapframecontent or '[]') ~= '[]' then addcomma=',' end
mapframecontent=getmapframecontent(args,'fullscreen')
local contentstart='[ '..mapframecontent..addcomma..'{ "type": "FeatureCollection", "features": [ ' --extra features after first square bracket
local contentend=' ] } ]'
table.insert(result, '<div style="position: absolute;top: 9px;left: '..tostring(maplist.width-34)..'px">')
table.insert(result, '<div style="color: white; opacity:100; font-size: 19px; font-weight:normal; text-align: left;">')
table.insert(result, frame:extensionTag{ name ='maplink', content=contentstart..table.concat(FullscreenList)..contentend, args={zoom=tostring(maplist.zoom+1), class='no-icon', frameless='1',
latitude=tostring(maplist.latbase), longitude=tostring(maplist.lonbase), --add invisble 'en-spaces' for tooltip
text='<div title="'..fullscreenlinktext..'"> </div>'} } )
table.insert(result,'</div></div>') -- end of maplink -----
-- add closing div for main map
table.insert(result,'</div>')
-- collate caption material to go in the outer div class
local autocaption=string.lower(args['auto-caption'] or 'no')
local autoOff=autocaption:match("(%w+)(.*)") -- select the first word in autocaption and see if it is a negativeAnswer)
if args.caption or not negativeAnswer[autoOff] then
table.insert(result,'<div class="thumbcaption" style="text-align:left">')
if args.caption then table.insert(result,args.caption) end
end
local columns=tonumber(autoOff:match("[%d]+") or '1')
if columns>1 then columns=round(maplist.width/(columns*17), 0) end -- convert from em to px for historical reasons
--for k in pairs(dotList) do capchk=capchk..(args["dotlink"..k] or '') end
local capchk=nil
local captionList = {}
for key, value in pairs(dotItemTable) do
-- only add an autocaption line if there is both a dotTag and a dotLink line available and it is not marked as nolist
if value.dotTag and value.dotTag~='' and (not string.find(value.param1 or '','nolist')) and string.gsub(value.dotLink or '',"%s+","")~='' then
table.insert(captionList, {key = key, value = value})
capchk=true
end
end
if capchk and (not negativeAnswer[autoOff]) then
table.sort(captionList, function (a,b) return lessthan(string.match(a.value.dotTag,'[%w]+'), string.match(b.value.dotTag,'[%w]+')) end)
local myDivision = string.gsub((args.toggletext or toggletext), "%s+", "")
if string.find(autocaption,'collaps') then
table.insert(result,'<hr><div class="mw-customtoggle-'..myDivision..'">'..(args.toggletext or toggletext)..'</div>')
if string.find(autocaption,'collapsed') then
table.insert(result,'<div class="mw-collapsible mw-collapsed" id="mw-customcollapsible-'..myDivision..'">')
else
table.insert(result,'<div class="mw-collapsible" id="mw-customcollapsible-'..myDivision..'">')
end
end
if string.find(autocaption, 'columns=') then
columns=string.match(autocaption,'[%d]+',string.find(autocaption, 'columns=') )
end
table.insert(result,'<div class="mw-collapsible-content" style="column-count:'..columns..'; column-rule:solid 1px;text-align:left;padding-top:5px">')
table.sort(dotList,lessthan)
local nval,ngrp='','0'
for k,v in pairs(captionList) do
nval=string.match(v.value.dotTag,'[%w]+') -- find the first alphanumeric item in the dotTag
ngrp=v.value.group or '0'
if v.value.dotLink and v.value.dotLink~='' and nval and nval~='' then
local c1,c2
if nval==args.highlight then
c1,c2=checkColors(shapeList['H'].shapeColor)
else
c1,c2=checkColors(shapeList[ngrp].shapeColor)
end
table.insert(result,'<div style="display:inline-block;line-height:110%;vertical-align:middle; padding:1px 4px;border-radius:8px;border: 0.5px solid black;')
table.insert(result,'background-color:'..c1..';color:'..c2..';font-size:88%;font-weight:bold">'..nval..'</div> '..v.value.dotLink..'<br>')
end
end
table.insert(result,'</div>') -- end for caption-content div
if string.find(autocaption,'collaps') then table.insert(result,'</div>') end -- end for toggle frame
end
if args.caption or not negativeAnswer[autoOff] then table.insert(result,'</div>') end -- end for whole caption frame
table.insert(result,'</div></div>') -- outer two frames
if args.float == 'center' or args.float=='centre' then table.insert(result,'</div>') end
if args['show-new-format'] == 'hints' then -- provide a 'format hint panel' in the 'Preview Box'
local w="<small>Below are some template hints for the 'sga' compressed version, "
w=w..'{{tl|OSM Location dots}}. It can use these, instead of the more verbose {{tl|OSM Location map}} parameter format. '
w=w..'Data is divided between a "ShapeGroup" and the "Dots", so that a single shapeGroup can be used for multiple dots on the map. '
previewMsg(w..'(nb. the "group" value can be the number or an assigned name of a shapeGroup)</small>')
w='<small>{{tl|OSM Location dots}}: | dot(n)=group,lat,lon,dotTag | '
w=w..'dotlink=link/tooltip | dotlabel=label,position,dx,dy,param1,info | dotpic=filename <br>'
w=w.."(nb. param1 options include 'nolink' 'nolist' 'nomap' 'hemisphere-1' 'hemisphere+1', 'noline' - quotes not required, separate with spaces).<br>"
w=w..'| sga = Shape,Sizepx,Color,Angledeg | sgb= OutlineWidth,Color,Style | sgc=TextSize,Color,Angle,bold italic <br>'
w=w..'| sgd=TagSizepx,Color,Spacer,Angledeg | sge=LineWidth,Color,Style | sgf=TextSpacingpx,LineHeight%,Outlinepx,backgroundColor<br>'
w=w..'| sgn=Name (optional, to assign a meaningful name) | sgp=Parent (can be the name or number of the parent shapeGroup. '
previewMsg(w..'Each shapegroup will inherit values from a parent, stretching back to "sga1" and its default values.)</small>')
if #pmsg then
local dbg={}
for i,x in pairs(pmsg) do
table.insert(dbg,x..'<br>')
end
dbg=mw.addWarning(table.concat(dbg))
table.insert(result, dbg)
end
end
if args.coordtest then debugmsg(mw.text.nowiki(args.coordtest)) end
for i,x in pairs(msg) do
table.insert(result, x..'<br>')
end
return table.concat(result)
end
function p.main(frame)
local args = getArgs(frame)
local itemTab={}
maplist.width=tonumber(args.width) or 400
maplist.height=tonumber(args.height) or 300
if args.coord then
itemTab=splitItem(convertCoordsTrad (args.coord),2)
maplist.latbase=itemTab[1]
maplist.lonbase=itemTab[2]
else
maplist.lonbase=tonumber(args.lon) or 5
maplist.latbase=tonumber(args.lat) or 0
end
maplist.zoom=tonumber(args.zoom) or 1
visibleLinks=args.showlinks
highlightNum=args.highlight
if args.nolabels=='1' then maplist.mapstyle='osm' else maplist.mapstyle='osm-intl' end
return p._main(args)
end
return p
ofjcmcbg4ar2hjutxobomnsg2e3ukvq
Szablóna:OSM Location map
10
13246
204467
2026-07-05T15:38:01Z
HaxelKaszëba
17492
Ùsôdzonô nowô starna "{{#invoke:OSM Location map|main|useFormatStyle={{{useFormatStyle|standardstyle}}} <noinclude> |label1=Annotated maps of anywhere in the world |mark-coord1={{coord|0|0}}|mark-size1=10|label-pos1=top</noinclude> }} {{Main other|[[Category:Articles containing OSM location maps]]}}<noinclude> [[Category:OSM Location map templates]] [[Category:Pages with disabled graphs]] </noinclude>"
204467
wikitext
text/x-wiki
{{#invoke:OSM Location map|main|useFormatStyle={{{useFormatStyle|standardstyle}}} <noinclude>
|label1=Annotated maps of anywhere in the world
|mark-coord1={{coord|0|0}}|mark-size1=10|label-pos1=top</noinclude> }}
{{Main other|[[Category:Articles containing OSM location maps]]}}<noinclude>
[[Category:OSM Location map templates]]
[[Category:Pages with disabled graphs]]
</noinclude>
bn3ie51w472y054e66h3l43q8qawo4d
Zelenin
0
13247
204477
2026-07-05T18:59:36Z
Terrus38
14026
Terrus38 przeniós starnã [[Zelenin]] do [[Zeleniô]]: Błędna pisownia tytułu: Zeleniô je zgòdą z http://skarbnicakaszubska.pl/uchwaa-nr-6-2010/
204477
wikitext
text/x-wiki
#PATRZ [[Zeleniô]]
mtvxz9w4tq4nqm7tz8hlnzz9izih0ye
Kategòrëjô:Meksyk
14
13248
204510
2026-07-06T11:55:41Z
Iketsi
3254
N
204510
wikitext
text/x-wiki
{{Commons}}
[[Kategòrëjô:Nordowô Amerika]]
l015vcgh0nkb7mdmh8waqx17j6ebdzl