mirror of
https://github.com/csharpee/Map-Projections.git
synced 2025-12-15 00:00:14 -05:00
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:
parent
78be3bb2f8
commit
b7cf4345fd
19
README.md
19
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).
|
||||
|
||||
2496
input/Landmasses.svg
2496
input/Landmasses.svg
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
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
1310
input/Tissot.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 607 KiB |
@ -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};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
123
src/zupplementary python/input_generator.py
Normal file
123
src/zupplementary python/input_generator.py
Normal 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)
|
||||
Loading…
x
Reference in New Issue
Block a user