diff --git a/README.md b/README.md
index 10f2e34..32b5b52 100644
--- a/README.md
+++ b/README.md
@@ -32,12 +32,13 @@ For some examples, check out the `output` folder. For more information, go to [j
## Credits
While I wrote all of the code in this repository myself, and I created several of the simpler images from scratch, other people did help. Here's a comprehensive list.
-* **The NASA** for [Basic.png](https://visibleearth.nasa.gov/view.php?id=57730), [Satellite.jpg](https://visibleearth.nasa.gov/view.php?id=57752), and [Altitude.png](https://asterweb.jpl.nasa.gov/gdem.asp)
-* **Tom Patterson** for [Pastel.jpg](http://www.shadedrelief.com/natural3/pages/textures.html) and [Rivers.png](http://www.shadedrelief.com/natural3/pages/extra.html)
-* **RokerHRO** for [the indicatrix layer of Tissot.jpg](https://commons.wikimedia.org/wiki/File:Tissot_10deg.png)
-* **Crates** for [Landmasses.svg and the landmass layer of Basic.svg](https://commons.wikimedia.org/wiki/File:World_map_blank_without_borders.svg)
-* **The CIA** for [Political.svg and Political.png](https://commons.wikimedia.org/wiki/File:BlankMap-World6-Equirectangular.svg).
-* **The Apache Commons** for their [complex mathematics code](https://commons.apache.org/proper/commons-math/)
-* **Technische Universität Berlin** for their [complex mathematics code](http://www3.math.tu-berlin.de/jtem/ellipticFunctions/)
-* **AuthaGraph Co., Ltd.** for their [vague information about their map projection](http://www.authagraph.com/projects/description/%E3%80%90%E4%BD%9C%E5%93%81%E8%A7%A3%E8%AA%AC%E3%80%91%E8%A8%98%E4%BA%8B01/?lang=en), which inspired several of my own.
-* **Wikipedia** for their [impressive collection of map projection information and equations](https://en.wikipedia.org/wiki/List_of_map_projections)
+* **The NASA** for [Basic.png](https://visibleearth.nasa.gov/view.php?id=57730), [Satellite.jpg](https://visibleearth.nasa.gov/view.php?id=57752), and [Altitude.png](https://asterweb.jpl.nasa.gov/gdem.asp),
+* **Tom Patterson** for [Pastel.jpg](http://www.shadedrelief.com/natural3/pages/textures.html) and [Rivers.png](http://www.shadedrelief.com/natural3/pages/extra.html),
+* **RokerHRO** for [the indicatrix layer of Tissot.jpg](https://commons.wikimedia.org/wiki/File:Tissot_10deg.png),
+* **Crates** for [Landmasses.svg](https://commons.wikimedia.org/wiki/File:World_map_blank_without_borders.svg), which I adapted into Basic.svg and Tissot.svg,
+* **The CIA** for [Political.svg and Political.png](https://commons.wikimedia.org/wiki/File:BlankMap-World6-Equirectangular.svg),
+* **The Apache Commons** for their [complex mathematics code](https://commons.apache.org/proper/commons-math/),
+* **Technische Universität Berlin** for their [complex mathematics code](http://www3.math.tu-berlin.de/jtem/ellipticFunctions/),
+* **Gene Keyes** for his [impressively in-depth and open documentation of his map projection](http://www.genekeyes.com/CKOG-OOo/7-CKOG-illus-&-coastline.html),
+* **AuthaGraph Co., Ltd.** for their [vague information about their map projection](http://www.authagraph.com/projects/description/%E3%80%90%E4%BD%9C%E5%93%81%E8%A7%A3%E8%AA%AC%E3%80%91%E8%A8%98%E4%BA%8B01/?lang=en), which inspired several of my own, and
+* **Wikipedia** for their [substantial collection of map projection information and equations](https://en.wikipedia.org/wiki/List_of_map_projections).
diff --git a/input/Landmasses.svg b/input/Landmasses.svg
index b1e30cc..c535a61 100644
--- a/input/Landmasses.svg
+++ b/input/Landmasses.svg
@@ -68,1253 +68,1253 @@
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/input/Orthodromes.svg b/input/Orthodromes.svg
new file mode 100644
index 0000000..8430e63
--- /dev/null
+++ b/input/Orthodromes.svg
@@ -0,0 +1,21 @@
+
+
+
diff --git a/input/Tissot.svg b/input/Tissot.svg
new file mode 100644
index 0000000..b73ec55
--- /dev/null
+++ b/input/Tissot.svg
@@ -0,0 +1,1310 @@
+
+
diff --git a/src/maps/Projection.java b/src/maps/Projection.java
index fdcdfdc..0f099b7 100644
--- a/src/maps/Projection.java
+++ b/src/maps/Projection.java
@@ -433,19 +433,16 @@ public abstract class Projection {
lonf = lon0;
}
else if (Math.sin(lon1) > 0)
- lonf = lon0 +
- Math.acos(innerFunc);
+ lonf = lon0 + Math.acos(innerFunc);
else
- lonf = lon0 -
- Math.acos(innerFunc);
+ lonf = lon0 - Math.acos(innerFunc);
if (Math.abs(lonf) > Math.PI)
lonf = Math2.floorMod(lonf+Math.PI, 2*Math.PI) - Math.PI;
double thtf = pole[2];
- double[] output = {latf, lonf, thtf};
- return output;
+ return new double[] {latf, lonf, thtf};
}
diff --git a/src/utils/SVGMap.java b/src/utils/SVGMap.java
index c746457..7b964e8 100644
--- a/src/utils/SVGMap.java
+++ b/src/utils/SVGMap.java
@@ -81,7 +81,7 @@ public class SVGMap implements Iterable {
@Override
public void startElement(
- String uri, String localName, String qName, Attributes attributes) {
+ String uri, String localName, String qName, Attributes attributes) throws SAXException {
currentFormatString += "<"+qName;
if (attributes.getIndex("transform") >= 0)
@@ -89,8 +89,13 @@ public class SVGMap implements Iterable {
else
parseTransform();
- if (qName.equals("path"))
- parsePath(attributes.getValue("d"));
+ if (qName.equals("path")) {
+ try {
+ parsePath(attributes.getValue("d"));
+ } catch (Exception e) {
+ throw new SAXException(e.getLocalizedMessage(), null);
+ }
+ }
if (qName.equals("svg")) {
parseViewBox(attributes);
@@ -181,7 +186,7 @@ public class SVGMap implements Iterable {
transformStack.push(new double[] {xScale, yScale, xTrans, yTrans});
}
- private void parsePath(String d) {
+ private void parsePath(String d) throws Exception {
currentFormatString += " d=\"";
format.add(currentFormatString);
paths.add(new Path(d, transformStack.peek(), minX, minY, width, height));
@@ -259,12 +264,12 @@ public class SVGMap implements Iterable {
super();
}
- public Path(String d, double vbWidth, double vbHeight) {
+ public Path(String d, double vbWidth, double vbHeight) throws Exception {
this(d, new double[] {1,1,0,0}, 0, 0, vbWidth, vbHeight);
}
public Path(String d, double[] transform,
- double vbMinX, double vbMinY, double vbWidth, double vbHeight) {
+ double vbMinX, double vbMinY, double vbWidth, double vbHeight) throws Exception { //I don't know if this is bad coding practice, but I don't really want to find a way to gracefully catch all possible errors into some more specific Exception class
super();
int i = 0;
@@ -276,6 +281,7 @@ public class SVGMap implements Iterable {
i ++;
argString += d.charAt(i);
}
+ argString = argString.replaceAll("([0-9\\.])-", "$1,-"); //this is necessary because some Adobe products leave out delimiters between negative numbers
i ++;
String[] argStrings;
@@ -288,11 +294,11 @@ public class SVGMap implements Iterable {
argStrings = new String[] {argStrings[3], argStrings[4]};
}
if (type == 'h' || type == 'H' || type == 'v' || type == 'V') { //convert these to 'L'
- final int chgIdx = (type%32 == 8) ? 0 : 1;
+ final int direcIdx = (type%32 == 8) ? 0 : 1;
args = new double[] {last[0], last[1]};
- if (type <= 'Z') args[chgIdx] = Double.parseDouble(argStrings[0]); //uppercase (absolute)
- else args[chgIdx] += Double.parseDouble(argStrings[0]); //lowercase (relative)
- last[chgIdx] = args[chgIdx];
+ if (type <= 'Z') args[direcIdx] = Double.parseDouble(argStrings[0]); //uppercase (absolute)
+ else args[direcIdx] += Double.parseDouble(argStrings[0]); //lowercase (relative)
+ last[direcIdx] = args[direcIdx];
type = 'L';
}
else {
diff --git a/src/zupplementary python/input_generator.py b/src/zupplementary python/input_generator.py
new file mode 100644
index 0000000..f2e7c05
--- /dev/null
+++ b/src/zupplementary python/input_generator.py
@@ -0,0 +1,123 @@
+import math
+import numpy as np
+
+PHI = (math.sqrt(5)+1)/2
+ATH = math.atan(1/2)
+
+IND_NUM = 120
+IND_RAD = 500/6371
+
+
+def obliquify(lat1, lon1, lat0, lon0):
+ """ go from relative to absolute coordinates """
+ latf = math.asin(math.sin(lat0)*math.sin(lat1) - math.cos(lat0)*math.cos(lon1)*math.cos(lat1))
+ innerFunc = math.sin(lat1)/math.cos(lat0)/math.cos(latf) - math.tan(lat0)*math.tan(latf)
+ if lat0 == math.pi/2: # accounts for special case when lat0 = pi/2
+ lonf = lon1+lon0
+ elif lat0 == -math.pi/2: # accounts for special case when lat0 = -pi/2
+ lonf = -lon1+lon0 + math.pi
+ elif abs(innerFunc) > 1: # accounts for special case when cos(lat1) -> 0
+ if (lon1 == 0 and lat1 < -lat0) or (lon1 != 0 and lat1 < lat0):
+ lonf = lon0 + math.pi
+ else:
+ lonf = lon0
+ elif math.sin(lon1) > 0:
+ lonf = lon0 + math.acos(innerFunc)
+ else:
+ lonf = lon0 - math.acos(innerFunc)
+
+ return latf, lonf
+
+
+def plot(coords):
+ tag = ' '
+ print(tag.replace('L', 'M', 1).replace('.000',''))
+
+
+def plot_orthodrome(phi0, lam0, tht0):
+ tag = ' '
+ print(tag.replace('L', 'M', 1))
+
+
+def plot_indicatrix(phi0, lam0, r):
+ tag = ' '
+ print(tag.replace('L', 'M', 1).replace('.000',''))
+
+
+def plot_side_indicatrix(phi0, r):
+ tag0 = ' '
+ print(tag0.replace('L', 'M', 1).replace('.000','') + tag1.replace('L', 'M', 1).replace('.000',''))
+
+
+def plot_pole_indicatrix(north, r):
+ if north:
+ yp, yr = 360, 360-2*math.degrees(r)
+ else:
+ yp, yr = 0, 2*math.degrees(r)
+
+ tag = ' '.format(720, yp)
+ print(tag)
+
+
+def plot_meridian(lamd):
+ tag = ' '
+ print(tag.replace('L', 'M', 1))
+
+
+def plot_parallel(phid):
+ tag = ' '
+ print(tag.replace('L', 'M', 1))
+
+
+if __name__ == '__main__':
+ # for l in range(0, 180, 36):
+ # plot_orthodrome(math.pi/2, 0, math.radians(l))
+ # for l in range(0, 360, 72):
+ # plot_orthodrome(ATH, math.radians(l), math.pi*.4)
+ # plot_orthodrome(ATH, math.radians(l), math.pi*.8)
+
+ plot_pole_indicatrix(False, IND_RAD)
+ for y in [60, 30, 0, -30, -60]:
+ for x in range(0, 360, 30):
+ # if x == 0:
+ # plot_side_indicatrix(math.radians(y), math.pi/36)
+ # else:
+ plot_indicatrix(math.radians(y), math.radians(x), IND_RAD)
+ plot_pole_indicatrix(True, IND_RAD)
+
+ # for x in range(0, 361, 10):
+ # plot_meridian(x)
+ # for y in range(-80, 90, 10):
+ # plot_parallel(y)