I make maps with _direction_ and MAGNITUDE

I added a few more vector inputs that I've been wanting for some time. My Tissot one I actually don't love, but it is based heavily on Eric Gaba's Tissot maps that are all over Wikipedia that I want to replace because they lack indicatrices in the most important locations: the poles and the prime meridian. After I fix Wikipedia, I might make a new one without a graticule and with more indicatrices.
I also made my program slightly better at loading new SVG maps and fixed Landmasses to not have a weird broken Antarctica.
This commit is contained in:
Justin Kunimune 2018-01-14 14:27:35 -10:00
parent 78be3bb2f8
commit b7cf4345fd
7 changed files with 2731 additions and 1273 deletions

View File

@ -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).

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 874 KiB

After

Width:  |  Height:  |  Size: 396 KiB

21
input/Orthodromes.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 86 KiB

1310
input/Tissot.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 607 KiB

View File

@ -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};
}

View File

@ -81,7 +81,7 @@ public class SVGMap implements Iterable<SVGMap.Path> {
@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<SVGMap.Path> {
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<SVGMap.Path> {
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<SVGMap.Path> {
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<SVGMap.Path> {
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<SVGMap.Path> {
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 {

View File

@ -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 = ' <path d="'
for phi, lam in coords:
tag += 'L{:.3f},{:.3f} '.format(2*math.degrees(lam), 2*math.degrees(phi)+180)
tag += 'Z"/>'
print(tag.replace('L', 'M', 1).replace('.000',''))
def plot_orthodrome(phi0, lam0, tht0):
tag = ' <path d="'
for p in range(90, -90, -1):
phin, lamn = obliquify(math.radians(p), tht0, phi0, lam0)
tag += 'L{:.3f},{:.3f} '.format(math.degrees(lamn), math.degrees(phin))
for p in range(-90, 90, 1):
phin, lamn = obliquify(math.radians(p), tht0+math.pi, phi0, lam0)
tag += 'L{:.3f},{:.3f} '.format(math.degrees(lamn), math.degrees(phin))
tag += 'Z"/>'
print(tag.replace('L', 'M', 1))
def plot_indicatrix(phi0, lam0, r):
tag = ' <path d="'
for l in range(0, 360, 360//IND_NUM):
phin, lamn = obliquify(math.pi/2-r, math.radians(l), phi0, lam0)
tag += 'L{:.3f},{:.3f} '.format(2*math.degrees(lamn), 2*math.degrees(phin)+180)
tag += 'Z"/>'
print(tag.replace('L', 'M', 1).replace('.000',''))
def plot_side_indicatrix(phi0, r):
tag0 = ' <path d="'
for l in range(0, 181, 360//IND_NUM):
phin, lamn = obliquify(math.pi/2-r, math.radians(l), phi0, 0)
tag0 += 'L{:.3f},{:.3f} '.format(2*math.degrees(lamn), 2*math.degrees(phin)+180)
tag1 = ''
for l in range(180, 361, 360//IND_NUM):
phin, lamn = obliquify(math.pi/2-r, math.radians(l), phi0, 0)
tag1 += 'L{:.3f},{:.3f} '.format(2*math.degrees(lamn)+720, 2*math.degrees(phin)+180)
tag1 += 'Z"/>'
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 = ' <path d="'
tag += 'M{},{} '.format(0, yp)
for x in range(0, 721, 360//IND_NUM):
tag += 'L{},{:.3f} '.format(x, yr)
tag += 'L{},{} Z"/>'.format(720, yp)
print(tag)
def plot_meridian(lamd):
tag = ' <path d="'
for phi in range(-90, 91):
tag += 'L{},{} '.format(2*lamd, 2*phi + 180)
tag += '"/>'
print(tag.replace('L', 'M', 1))
def plot_parallel(phid):
tag = ' <path d="'
for lam in range(0, 361):
tag += 'L{},{} '.format(2*lam, 2*phid + 180)
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)