From an ESRI shapefile of world countries I have created, in R,
library(sf)
world <- st_read("World_Countries__Generalized_.shp", stringsAsFactors=FALSE)
cs<-c("af", "al", "dz", "as", "ad", "ao", "ai", "aq", "ag", "ar", "am", "aw", "au", "at", "az", "pt", "bs", "bh", "bd", "bb", "by", "be", "bz", "bj", "bm", "bt", "bo", "bq", "ba", "bw", "bv", "br", "io", "vg", "bn", "bg", "bf", "bi", "cv", "kh", "cm", "ca", "es", "ky", "cf", "td", "cl", "cn", "cx", "cc", "co", "km", "cg", "cd", "ck", "cr", "ci", "hr", "cu", "cw", "cy", "cz", "dk", "dj", "dm", "do", "ec", "eg", "sv", "gq", "er", "ee", "sz", "et", "fk", "fo", "fj", "fi", "fr", "gf", "pf", "tf", "ga", "gm", "ge", "de", "gh", "gi", "tf", "gr", "gl", "gd", "gp", "gu", "gt", "gg", "gn", "gw", "gy", "ht", "hm", "hn", "hu", "is", "in", "id", "ir", "iq", "ie", "im", "il", "it", "jm", "jp", "je", "jo", "tf", "kz", "ke", "ki", "kw", "kg", "la", "lv", "lb", "ls", "lr", "ly", "li", "lt", "lu", "mg", "pt", "mw", "my", "mv", "ml", "mt", "mh", "mq", "mr", "mu", "yt", "mx", "fm", "md", "mc", "mn", "me", "ms", "ma", "mz", "mm", "na", "nr", "np", "nl", "nc", "nz", "ni", "ne", "ng", "nu", "nf", "kp", "mk", "mp", "no", "om", "pk", "pw", "ps", "pa", "pg", "py", "pe", "ph", "pn", "pl", "pt", "pr", "qa", "re", "ro", "ru", "rw", "bq", "bl", "bq", "sh", "kn", "lc", "mf", "pm", "vc", "ws", "sm", "st", "sa", "sn", "rs", "sc", "sl", "sg", "sx", "sk", "si", "sb", "so", "za", "gs", "kr", "ss", "es", "lk", "sd", "sr", "sj", "se", "ch", "sy", "tj", "tz", "th", "tl", "tg", "tk", "to", "tt", "tn", "tr", "tm", "tc", "tv", "ug", "ua", "ae", "gb", "us", "um", "uy", "vi", "uz", "vu", "va", "ve", "vn", "wf", "ye", "zm", "zw")
for(i in 1:length(cs)){
dput(world$geometry[[i]],file=paste0(cs[i],".R"))
}
no<-source("no.R")$value
cat(print(no),file = "no.txt")
files isocode.txt
(i.e. no.txt
for Norway) containing strings
"MULTIPOLYGON (((X1 Y1, X2 Y2, ...), ((XX1 YY1, ...) ))"
and I want to turn them into
"no={Polyline((radius; X1°; X2°), ...),Polyline((radius; XX1°;YY1°), ...)}"
for use in geogebra to draw on the sphere x^2+y^2+z^2=radius^2
. Say with radius=5
.
My work: for proof of concept I used some horribly slow emacs lisp keyboard macros.
I don't really know common lisp, but I heard it is good for these kinds of problems.
Edit
Edit 2
I've gotten a bit more ambitious and want to write to the geogebra.xml in the world.ggb directly (it's just a .zip of some files; this being one of them). For each line of world2.txt
it should have an entry of the form
<command name="PolyLine">
<input [my output goes here]\>
<output a0="aq[the iso code for antarctica]"/>
</command>
<element type="polyline3d" label="aq">
<lineStyle thickness="1" type="0" typeHidden="1" opacity="178"/>
<show object="true" label="false" ev="4"/>
<objColor r="0" g="0" b="0" alpha="0.0"/>
<layer val="0"/>
<labelMode val="0"/>
</element>
where world2.txt
just keeps lines of these MULTIPOLYGON
s.
There a few problems with my implementation, it only handles the first polygon in the list, and it feels a bit hacky. Also the list of names (iso codes) in the order of the lines in the file should go in there.
Edit 3
I have worked out an overly complicated solution for my question. The geogebra.xml
needs some postprocessing to get rid of trailing commas and adding a header and a footer.
Even more ambitiously: I now want to read directly from a shapefile.
(ql:quickload "split-sequence")
(ql:quickload "parse-number")
(defun extract-lon (str)
(let ((m (search " " str)))
(parse-number:parse-real-number (subseq str 0 m))))
(defun extract-lat (str)
(let ((m (search " " str))
(l (length str)))
(parse-number:parse-real-number (subseq str (1+ m) l))))
(defun remove-junk (string)
(string-right-trim ")" (string-right-trim " " (string-right-trim "^M" (string-right-trim ")" (string-left-trim "(" (string-left-trim " " string)))))))
(defun split (delimiter-string string)
"Arthur Lemmens idea extended to splitting on substrings, better version due to pjb from #clschool"
(loop with len = (length string)
with dlen = (length delimiter-string)
with left = 0
while (< left len)
collect (let ((right (or (search delimiter-string string :start2 left)
len)))
(prog1 (subseq string left right)
(setf left (+ right dlen))))))
(defparameter *isocodes* '("AQ" "AF" "AL" "DZ" "AS" "AD" "AO" "AI" "AG" "AR" "AM" "AW" "AU" "AT" "AZ" "PT" "BS" "BH" "BD" "BB" "BY" "BE" "BZ" "BJ" "BM" "BT" "BO" "BQ" "BA" "BW" "BV" "BR" "IO" "VG" "BN" "BG" "BF" "BI" "CV" "KH" "CM" "CA" "ES" "KY" "CF" "TD" "CL" "CN" "CX" "CC" "CO" "KM" "CG" "CD" "CK" "CR" "CI" "HR" "CU" "CW" "CY" "CZ" "DK" "DJ" "DM" "DO" "EC" "EG" "SV" "GQ" "ER" "EE" "SZ" "ET" "FK" "FO" "FJ" "FI" "FR" "GF" "PF" "TF" "GA" "GM" "GE" "DE" "GH" "GI" "TF" "GR" "GL" "GD" "GP" "GU" "GT" "GG" "GN" "GW" "GY" "HT" "HM" "HN" "HU" "IS" "IN" "ID" "IR" "IQ" "IE" "IM" "IL" "IT" "JM" "JP" "JE" "JO" "TF" "KZ" "KE" "KI" "KW" "KG" "LA" "LV" "LB" "LS" "LR" "LY" "LI" "LT" "LU" "MG" "PT" "MW" "MY" "MV" "ML" "MT" "MH" "MQ" "MR" "MU" "YT" "MX" "FM" "MD" "MC" "MN" "ME" "MS" "MA" "MZ" "MM" "NA" "NR" "NP" "NL" "NC" "NZ" "NI" "NE" "NG" "NU" "NF" "KP" "MK" "MP" "NO" "OM" "PK" "PW" "PS" "PA" "PG" "PY" "PE" "PH" "PN" "PL" "PT" "PR" "QA" "RE" "RO" "RU" "RW" "BQ" "BL" "BQ" "SH" "KN" "LC" "MF" "PM" "VC" "WS" "SM" "ST" "SA" "SN" "RS" "SC" "SL" "SG" "SX" "SK" "SI" "SB" "SO" "ZA" "GS" "KR" "SS" "ES" "LK" "SD" "SR" "SJ" "SE" "CH" "SY" "TJ" "TZ" "TH" "TL" "TG" "TK" "TO" "TT" "TN" "TR" "TM" "TC" "TV" "UG" "UA" "AE" "GB" "US" "UM" "UY" "VI" "UZ" "VU" "VA" "VE" "VN" "WF" "YE" "ZM" "ZW"))
(with-open-file (in "c:/Users/nmajo/Dropbox/World_Countries_(Generalized)/world3.txt" :direction :input)
(with-open-file (out "c:/Users/nmajo/Dropbox/World_Countries_(Generalized)/geogebra.xml" :direction :output)
(let ((sexpr (loop for text = (read-line in nil)
while text collect
(loop for string in (mapcar #'remove-junk (split "))," (subseq text (length "MULTIPOLYGON "))))
collect
(loop for substring in (split-sequence:split-sequence #\, string)
collect `(,(extract-lon (string-left-trim " " substring)) ,(extract-lat (string-left-trim " " substring))))))))
(loop for iso in sexpr
for i from 0
do (format out "<expression label=~s exp=\"{" (nth i *isocodes*))
(loop for polygon in iso
do (format out "PolyLine[")
(loop for coordinates in polygon
do (eval `(format ,out "(5; ~f°; ~f°)," ,@coordinates)))
(format out "],"))
(format out "}\" />
<element type=\"list\" label=~s>
<show object=\"true\" label=\"true\" ev=\"4\"/>
<objColor r=\"0\" g=\"0\" b=\"0\" alpha=\"0.0\"/>
<layer val=\"0\"/>
<labelMode val=\"0\"/>
<lineStyle thickness=\"1\" type=\"0\" typeHidden=\"1\"/>
<pointSize val=\"5\"/>
<pointStyle val=\"0\"/>
<angleStyle val=\"0\"/>
</element>" (nth i *isocodes*))))))