So... many... maps...
I decided to spruce up my input SVG generation code, and got very carried away. I moved some of the bigger or more specific files into an Advanced folder in input, and then I got the horrible idea to make a super detailed computer-generated map from Natural Earth data with labels for everything and fancy borders, and then I had to do it. It took a very long time. But it is complete. Check out Advanced\Supermap if you have a powerful enough computer. I also reorganised my code a bit and took better advantage of shapefiles.
@ -43,8 +43,8 @@ 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 [Clouds.jpg](http://www.shadedrelief.com/natural3/pages/textures.html), [Rivers.png](http://www.shadedrelief.com/natural3/pages/extra.html), and [Political.svg](https://commons.wikimedia.org/wiki/File:BlankMap-Equirectangular.svg),
|
||||
* **Natural Earth** for [Pastel.png](http://www.naturalearthdata.com/downloads/50m-raster-data/50m-natural-earth-2/) and their [vector coastline data](http://www.naturalearthdata.com/downloads/50m-physical-vectors/),
|
||||
* **Tom Patterson** for [Clouds.jpg](http://www.shadedrelief.com/natural3/pages/textures.html), [Rivers.png](http://www.shadedrelief.com/natural3/pages/extra.html),
|
||||
* **Natural Earth** for [Pastel.png](http://www.naturalearthdata.com/downloads/50m-raster-data/50m-natural-earth-2/) and their [detailed vector data](http://www.naturalearthdata.com/downloads/),
|
||||
* **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 documentation of his map projection](http://www.genekeyes.com/CKOG-OOo/7-CKOG-illus-&-coastline.html),
|
||||
|
||||
6319
input/Advanced/Supermap.svg
Normal file
|
After Width: | Height: | Size: 4.8 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 544 KiB After Width: | Height: | Size: 544 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
334
input/Advanced/Tissot Wikipedia +0.svg
Normal file
|
After Width: | Height: | Size: 164 KiB |
334
input/Advanced/Tissot Wikipedia -20.svg
Normal file
|
After Width: | Height: | Size: 164 KiB |
3012
input/Basic.svg
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.1 MiB |
3223
input/Compound.svg
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.3 MiB |
@ -15,10 +15,10 @@
|
||||
<rdf:Description
|
||||
about="https://github.com/jkunimune15/Map-Projections/"
|
||||
dc:title="Equirectangular map - 5 degree graticule"
|
||||
dc:description="An Equirectangular graticule, showing all landmasses with 5 degree graticule."
|
||||
dc:description="An Equirectangular graticule, showing all landmasses with 5 degree graticule, including the Polar Circles and the Tropics."
|
||||
dc:creator="Justin Kunimune"
|
||||
dc:source="http://www.naturalearthdata.com/"
|
||||
dc:date="2018-01-15"
|
||||
dc:date="2018-02-04"
|
||||
dc:format="image/svg+xml"
|
||||
dc:language="en" >
|
||||
</rdf:Description>
|
||||
@ -35,7 +35,7 @@
|
||||
</metadata>
|
||||
<title>Equirectangular map – 5° graticule</title>
|
||||
<desc>
|
||||
An Equirectangular graticule, showing all landmasses with 5° graticule, including the Arctic Circle, the Antarctic Circle, and the Tropics.
|
||||
An Equirectangular graticule, showing all landmasses with 5° graticule, including the Polar Circles and the Tropics.
|
||||
</desc>
|
||||
<style>
|
||||
.graticule {
|
||||
@ -53,11 +53,11 @@
|
||||
}
|
||||
.tropics {
|
||||
stroke:#bf0000;
|
||||
stroke-dasharray:2;
|
||||
stroke-dasharray:1;
|
||||
}
|
||||
.circles {
|
||||
stroke:#0000bf;
|
||||
stroke-dasharray:2;
|
||||
stroke-dasharray:1;
|
||||
}
|
||||
</style>
|
||||
<g transform="matrix(1,0,0,-1,180,90)">
|
||||
@ -168,7 +168,6 @@
|
||||
<path d="M-180,85h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1" />
|
||||
<path class="prime-m" d="M-180,-90v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1" />
|
||||
<path class="prime-m" d="M0,-90v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1" />
|
||||
<path class="prime-m" d="M180,-90v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1v1" />
|
||||
<path class="equator" d="M-180,0h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1" />
|
||||
<path class="tropics" d="M-180,-23.43694h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1" />
|
||||
<path class="tropics" d="M-180,23.43694h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1h1" />
|
||||
|
||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 52 KiB |
@ -18,7 +18,7 @@
|
||||
dc:description="A mesh of great circles that intersect with 10-fold symmetry at 12 principal nodes and 6-fold symmetry at 20 minor nodes. Aligns with the edges of an icosohedron and its dual dodecahedron."
|
||||
dc:creator="Justin Kunimune"
|
||||
dc:source="http://www.naturalearthdata.com/"
|
||||
dc:date="2018-01-15"
|
||||
dc:date="2018-02-04"
|
||||
dc:format="image/svg+xml"
|
||||
dc:language="en" >
|
||||
</rdf:Description>
|
||||
@ -33,17 +33,18 @@
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<title>Icosohedral orthodromic mesh - Equirectangular projection</title>
|
||||
<title>Icosohedral orthodromic mesh – Equirectangular projection</title>
|
||||
<desc>
|
||||
A mesh of great circles that intersect with 10-fold symmetry at 12 principal nodes and 6-fold symmetry at 20 minor nodes. Aligns with the edges of an icosohedron and its dual dodecahedron.
|
||||
</desc>
|
||||
<style>
|
||||
path {
|
||||
.lines {
|
||||
stroke:#000000;
|
||||
fill:none;
|
||||
}
|
||||
</style>
|
||||
<g transform="matrix(1,0,0,-1,180,90)">
|
||||
<g class="lines">
|
||||
<path d="M-180,-90 L-180,-89 L-180,-88 L-180,-87 L-180,-86 L-180,-85 L-180,-84 L-180,-83 L-180,-82 L-180,-81 L-180,-80 L-180,-79 L-180,-78 L-180,-77 L-180,-76 L-180,-75 L-180,-74 L-180,-73 L-180,-72 L-180,-71 L-180,-70 L-180,-69 L-180,-68 L-180,-67 L-180,-66 L-180,-65 L-180,-64 L-180,-63 L-180,-62 L-180,-61 L-180,-60 L-180,-59 L-180,-58 L-180,-57 L-180,-56 L-180,-55 L-180,-54 L-180,-53 L-180,-52 L-180,-51 L-180,-50 L-180,-49 L-180,-48 L-180,-47 L-180,-46 L-180,-45 L-180,-44 L-180,-43 L-180,-42 L-180,-41 L-180,-40 L-180,-39 L-180,-38 L-180,-37 L-180,-36 L-180,-35 L-180,-34 L-180,-33 L-180,-32 L-180,-31 L-180,-30 L-180,-29 L-180,-28 L-180,-27 L-180,-26 L-180,-25 L-180,-24 L-180,-23 L-180,-22 L-180,-21 L-180,-20 L-180,-19 L-180,-18 L-180,-17 L-180,-16 L-180,-15 L-180,-14 L-180,-13 L-180,-12 L-180,-11 L-180,-10 L-180,-9 L-180,-8 L-180,-7 L-180,-6 L-180,-5 L-180,-4 L-180,-3 L-180,-2 L-180,-1 L-180,0 L-180,1 L-180,2 L-180,3 L-180,4 L-180,5 L-180,6 L-180,7 L-180,8 L-180,9 L-180,10 L-180,11 L-180,12 L-180,13 L-180,14 L-180,15 L-180,16 L-180,17 L-180,18 L-180,19 L-180,20 L-180,21 L-180,22 L-180,23 L-180,24 L-180,25 L-180,26 L-180,27 L-180,28 L-180,29 L-180,30 L-180,31 L-180,32 L-180,33 L-180,34 L-180,35 L-180,36 L-180,37 L-180,38 L-180,39 L-180,40 L-180,41 L-180,42 L-180,43 L-180,44 L-180,45 L-180,46 L-180,47 L-180,48 L-180,49 L-180,50 L-180,51 L-180,52 L-180,53 L-180,54 L-180,55 L-180,56 L-180,57 L-180,58 L-180,59 L-180,60 L-180,61 L-180,62 L-180,63 L-180,64 L-180,65 L-180,66 L-180,67 L-180,68 L-180,69 L-180,70 L-180,71 L-180,72 L-180,73 L-180,74 L-180,75 L-180,76 L-180,77 L-180,78 L-180,79 L-180,80 L-180,81 L-180,82 L-180,83 L-180,84 L-180,85 L-180,86 L-180,87 L-180,88 L-180,89 L-180,90 " />
|
||||
<path d="M-144,-90 L-144,-89 L-144,-88 L-144,-87 L-144,-86 L-144,-85 L-144,-84 L-144,-83 L-144,-82 L-144,-81 L-144,-80 L-144,-79 L-144,-78 L-144,-77 L-144,-76 L-144,-75 L-144,-74 L-144,-73 L-144,-72 L-144,-71 L-144,-70 L-144,-69 L-144,-68 L-144,-67 L-144,-66 L-144,-65 L-144,-64 L-144,-63 L-144,-62 L-144,-61 L-144,-60 L-144,-59 L-144,-58 L-144,-57 L-144,-56 L-144,-55 L-144,-54 L-144,-53 L-144,-52 L-144,-51 L-144,-50 L-144,-49 L-144,-48 L-144,-47 L-144,-46 L-144,-45 L-144,-44 L-144,-43 L-144,-42 L-144,-41 L-144,-40 L-144,-39 L-144,-38 L-144,-37 L-144,-36 L-144,-35 L-144,-34 L-144,-33 L-144,-32 L-144,-31 L-144,-30 L-144,-29 L-144,-28 L-144,-27 L-144,-26 L-144,-25 L-144,-24 L-144,-23 L-144,-22 L-144,-21 L-144,-20 L-144,-19 L-144,-18 L-144,-17 L-144,-16 L-144,-15 L-144,-14 L-144,-13 L-144,-12 L-144,-11 L-144,-10 L-144,-9 L-144,-8 L-144,-7 L-144,-6 L-144,-5 L-144,-4 L-144,-3 L-144,-2 L-144,-1 L-144,0 L-144,1 L-144,2 L-144,3 L-144,4 L-144,5 L-144,6 L-144,7 L-144,8 L-144,9 L-144,10 L-144,11 L-144,12 L-144,13 L-144,14 L-144,15 L-144,16 L-144,17 L-144,18 L-144,19 L-144,20 L-144,21 L-144,22 L-144,23 L-144,24 L-144,25 L-144,26 L-144,27 L-144,28 L-144,29 L-144,30 L-144,31 L-144,32 L-144,33 L-144,34 L-144,35 L-144,36 L-144,37 L-144,38 L-144,39 L-144,40 L-144,41 L-144,42 L-144,43 L-144,44 L-144,45 L-144,46 L-144,47 L-144,48 L-144,49 L-144,50 L-144,51 L-144,52 L-144,53 L-144,54 L-144,55 L-144,56 L-144,57 L-144,58 L-144,59 L-144,60 L-144,61 L-144,62 L-144,63 L-144,64 L-144,65 L-144,66 L-144,67 L-144,68 L-144,69 L-144,70 L-144,71 L-144,72 L-144,73 L-144,74 L-144,75 L-144,76 L-144,77 L-144,78 L-144,79 L-144,80 L-144,81 L-144,82 L-144,83 L-144,84 L-144,85 L-144,86 L-144,87 L-144,88 L-144,89 L-144,90 " />
|
||||
<path d="M-108,-90 L-108,-89 L-108,-88 L-108,-87 L-108,-86 L-108,-85 L-108,-84 L-108,-83 L-108,-82 L-108,-81 L-108,-80 L-108,-79 L-108,-78 L-108,-77 L-108,-76 L-108,-75 L-108,-74 L-108,-73 L-108,-72 L-108,-71 L-108,-70 L-108,-69 L-108,-68 L-108,-67 L-108,-66 L-108,-65 L-108,-64 L-108,-63 L-108,-62 L-108,-61 L-108,-60 L-108,-59 L-108,-58 L-108,-57 L-108,-56 L-108,-55 L-108,-54 L-108,-53 L-108,-52 L-108,-51 L-108,-50 L-108,-49 L-108,-48 L-108,-47 L-108,-46 L-108,-45 L-108,-44 L-108,-43 L-108,-42 L-108,-41 L-108,-40 L-108,-39 L-108,-38 L-108,-37 L-108,-36 L-108,-35 L-108,-34 L-108,-33 L-108,-32 L-108,-31 L-108,-30 L-108,-29 L-108,-28 L-108,-27 L-108,-26 L-108,-25 L-108,-24 L-108,-23 L-108,-22 L-108,-21 L-108,-20 L-108,-19 L-108,-18 L-108,-17 L-108,-16 L-108,-15 L-108,-14 L-108,-13 L-108,-12 L-108,-11 L-108,-10 L-108,-9 L-108,-8 L-108,-7 L-108,-6 L-108,-5 L-108,-4 L-108,-3 L-108,-2 L-108,-1 L-108,0 L-108,1 L-108,2 L-108,3 L-108,4 L-108,5 L-108,6 L-108,7 L-108,8 L-108,9 L-108,10 L-108,11 L-108,12 L-108,13 L-108,14 L-108,15 L-108,16 L-108,17 L-108,18 L-108,19 L-108,20 L-108,21 L-108,22 L-108,23 L-108,24 L-108,25 L-108,26 L-108,27 L-108,28 L-108,29 L-108,30 L-108,31 L-108,32 L-108,33 L-108,34 L-108,35 L-108,36 L-108,37 L-108,38 L-108,39 L-108,40 L-108,41 L-108,42 L-108,43 L-108,44 L-108,45 L-108,46 L-108,47 L-108,48 L-108,49 L-108,50 L-108,51 L-108,52 L-108,53 L-108,54 L-108,55 L-108,56 L-108,57 L-108,58 L-108,59 L-108,60 L-108,61 L-108,62 L-108,63 L-108,64 L-108,65 L-108,66 L-108,67 L-108,68 L-108,69 L-108,70 L-108,71 L-108,72 L-108,73 L-108,74 L-108,75 L-108,76 L-108,77 L-108,78 L-108,79 L-108,80 L-108,81 L-108,82 L-108,83 L-108,84 L-108,85 L-108,86 L-108,87 L-108,88 L-108,89 L-108,90 " />
|
||||
@ -75,4 +76,5 @@
|
||||
<path d="M108,-26.565 L108.653,-25.755 L109.296,-24.941 L109.932,-24.125 L110.559,-23.306 L111.178,-22.485 L111.791,-21.662 L112.396,-20.836 L112.994,-20.008 L113.587,-19.178 L114.173,-18.347 L114.754,-17.513 L115.329,-16.678 L115.900,-15.841 L116.466,-15.003 L117.027,-14.164 L117.584,-13.323 L118.138,-12.481 L118.687,-11.637 L119.234,-10.793 L119.777,-9.948 L120.318,-9.102 L120.856,-8.255 L121.391,-7.408 L121.925,-6.559 L122.457,-5.711 L122.987,-4.861 L123.516,-4.012 L124.044,-3.162 L124.571,-2.311 L125.097,-1.461 L125.623,-0.610 L126.149,0.240 L126.674,1.091 L127.200,1.941 L127.727,2.792 L128.254,3.642 L128.783,4.492 L129.313,5.341 L129.844,6.190 L130.376,7.039 L130.911,7.887 L131.448,8.734 L131.988,9.580 L132.530,10.426 L133.075,11.270 L133.623,12.114 L134.175,12.957 L134.730,13.798 L135.290,14.638 L135.853,15.477 L136.422,16.314 L136.995,17.150 L137.574,17.984 L138.157,18.817 L138.747,19.648 L139.343,20.476 L139.945,21.303 L140.555,22.127 L141.171,22.950 L141.795,23.769 L142.426,24.587 L143.066,25.401 L143.715,26.213 L144.373,27.022 L145.040,27.827 L145.717,28.630 L146.405,29.429 L147.104,30.224 L147.814,31.016 L148.535,31.804 L149.270,32.587 L150.017,33.367 L150.777,34.141 L151.552,34.911 L152.341,35.676 L153.145,36.436 L153.966,37.190 L154.802,37.938 L155.656,38.681 L156.528,39.417 L157.418,40.146 L158.328,40.868 L159.257,41.584 L160.207,42.291 L161.179,42.990 L162.173,43.681 L163.190,44.364 L164.230,45.037 L165.296,45.700 L166.386,46.353 L167.503,46.996 L168.647,47.627 L169.818,48.247 L171.018,48.854 L172.247,49.449 L173.506,50.031 L174.795,50.598 L176.116,51.151 L177.468,51.689 L178.852,52.211 L-179.732,52.716 L-178.283,53.204 L-176.801,53.674 L-175.286,54.126 L-173.739,54.558 L-172.160,54.969 L-170.548,55.360 L-168.905,55.729 L-167.232,56.076 L-165.530,56.400 L-163.799,56.701 L-162.042,56.976 L-160.260,57.227 L-158.454,57.453 L-156.628,57.652 L-154.782,57.824 L-152.921,57.970 L-151.045,58.088 L-149.159,58.178 L-147.264,58.241 L-145.365,58.275 L-143.463,58.281 L-141.562,58.259 L-139.664,58.209 L-137.774,58.131 L-135.893,58.025 L-134.025,57.891 L-132.173,57.730 L-130.338,57.542 L-128.523,57.328 L-126.730,57.089 L-124.962,56.824 L-123.219,56.534 L-121.505,56.220 L-119.819,55.883 L-118.163,55.523 L-116.537,55.142 L-114.944,54.739 L-113.383,54.316 L-111.854,53.873 L-110.358,53.411 L-108.895,52.931 L-107.464,52.433 L-106.066,51.918 L-104.700,51.387 L-103.366,50.841 L-102.063,50.279 L-100.791,49.704 L-99.549,49.115 L-98.336,48.513 L-97.153,47.898 L-95.997,47.272 L-94.869,46.634 L-93.767,45.985 L-92.691,45.326 L-91.639,44.658 L-90.612,43.979 L-89.608,43.292 L-88.627,42.596 L-87.668,41.892 L-86.730,41.180 L-85.812,40.461 L-84.913,39.735 L-84.033,39.002 L-83.172,38.262 L-82.328,37.516 L-81.500,36.765 L-80.689,36.007 L-79.893,35.245 L-79.112,34.477 L-78.346,33.704 L-77.593,32.927 L-76.853,32.145 L-76.126,31.359 L-75.411,30.569 L-74.708,29.775 L-74.015,28.978 L-73.333,28.177 L-72.662,27.373 L-72,26.565 " />
|
||||
<path d="M108,-26.565 L109.060,-26.252 L110.115,-25.931 L111.164,-25.603 L112.207,-25.267 L113.244,-24.924 L114.276,-24.574 L115.301,-24.217 L116.321,-23.853 L117.335,-23.483 L118.344,-23.105 L119.346,-22.722 L120.343,-22.332 L121.335,-21.936 L122.321,-21.535 L123.301,-21.127 L124.276,-20.714 L125.246,-20.296 L126.210,-19.872 L127.169,-19.443 L128.123,-19.009 L129.072,-18.570 L130.017,-18.126 L130.956,-17.678 L131.891,-17.225 L132.821,-16.768 L133.747,-16.307 L134.668,-15.842 L135.585,-15.373 L136.498,-14.901 L137.407,-14.424 L138.312,-13.945 L139.213,-13.462 L140.111,-12.975 L141.005,-12.486 L141.895,-11.994 L142.783,-11.499 L143.667,-11.001 L144.548,-10.501 L145.427,-9.998 L146.303,-9.493 L147.176,-8.986 L148.047,-8.476 L148.915,-7.965 L149.781,-7.452 L150.645,-6.938 L151.508,-6.421 L152.368,-5.904 L153.227,-5.385 L154.085,-4.865 L154.941,-4.343 L155.796,-3.821 L156.650,-3.298 L157.503,-2.774 L158.355,-2.250 L159.207,-1.725 L160.058,-1.200 L160.909,-0.674 L161.760,-0.149 L162.610,0.377 L163.461,0.903 L164.312,1.428 L165.163,1.953 L166.015,2.478 L166.868,3.002 L167.721,3.526 L168.576,4.048 L169.431,4.570 L170.288,5.091 L171.146,5.611 L172.006,6.129 L172.867,6.646 L173.730,7.162 L174.595,7.676 L175.463,8.188 L176.332,8.698 L177.204,9.206 L178.078,9.713 L178.955,10.217 L179.835,10.718 L-179.283,11.218 L-178.397,11.714 L-177.508,12.208 L-176.616,12.699 L-175.721,13.187 L-174.821,13.672 L-173.919,14.154 L-173.012,14.632 L-172.101,15.107 L-171.187,15.578 L-170.268,16.045 L-169.345,16.508 L-168.417,16.968 L-167.485,17.423 L-166.548,17.873 L-165.607,18.320 L-164.660,18.761 L-163.709,19.198 L-162.753,19.630 L-161.791,20.057 L-160.824,20.478 L-159.853,20.894 L-158.875,21.305 L-157.892,21.710 L-156.904,22.109 L-155.910,22.502 L-154.911,22.889 L-153.906,23.270 L-152.895,23.645 L-151.878,24.012 L-150.856,24.373 L-149.828,24.727 L-148.794,25.075 L-147.754,25.414 L-146.708,25.747 L-145.657,26.072 L-144.600,26.389 L-143.537,26.699 L-142.468,27 L-141.394,27.294 L-140.314,27.579 L-139.229,27.856 L-138.138,28.124 L-137.041,28.384 L-135.940,28.634 L-134.833,28.876 L-133.721,29.109 L-132.604,29.333 L-131.482,29.547 L-130.356,29.751 L-129.225,29.947 L-128.090,30.132 L-126.951,30.308 L-125.808,30.474 L-124.661,30.629 L-123.510,30.775 L-122.356,30.910 L-121.199,31.036 L-120.039,31.150 L-118.876,31.255 L-117.711,31.349 L-116.544,31.432 L-115.374,31.505 L-114.203,31.567 L-113.031,31.619 L-111.857,31.659 L-110.683,31.689 L-109.508,31.709 L-108.332,31.717 L-107.157,31.715 L-105.981,31.702 L-104.806,31.678 L-103.632,31.643 L-102.459,31.598 L-101.287,31.541 L-100.117,31.475 L-98.948,31.397 L-97.782,31.309 L-96.618,31.211 L-95.456,31.102 L-94.297,30.982 L-93.142,30.853 L-91.989,30.713 L-90.840,30.563 L-89.695,30.403 L-88.553,30.233 L-87.415,30.053 L-86.282,29.863 L-85.153,29.664 L-84.029,29.455 L-82.910,29.236 L-81.795,29.009 L-80.685,28.772 L-79.581,28.526 L-78.481,28.272 L-77.387,28.008 L-76.299,27.736 L-75.216,27.456 L-74.138,27.167 L-73.066,26.870 L-72,26.565 " />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
1260
input/Political.svg
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
3431
input/Tissot.svg
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 1.3 MiB |
774
output/Unbiased Political.svg
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
143
src/zupplemental/compose_maps.py
Normal file
@ -0,0 +1,143 @@
|
||||
#compose_maps.py
|
||||
#make ALL the maps
|
||||
|
||||
import math
|
||||
|
||||
from generate_borders import generate_borders
|
||||
from generate_graticule import generate_graticule
|
||||
from generate_indicatrices import generate_indicatrices
|
||||
from generate_orthodromes import generate_orthodromes
|
||||
from generate_shape import generate_administrivia, generate_disputes, generate_fine_borders, generate_land, generate_rivers, generate_lakes
|
||||
from generate_labels import generate_topographical_labels, generate_political_labels, generate_urban_labels
|
||||
|
||||
|
||||
def compose_landmasses():
|
||||
print('\t<g transform="matrix(1,0,0,-1,180,90)">')
|
||||
print('\t\t<g class="land">')
|
||||
generate_land('ne_50m', trim_antarctica=True)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="water">')
|
||||
generate_lakes('ne_50m', max_rank=4)
|
||||
print('\t\t</g>')
|
||||
print('\t</g>')
|
||||
|
||||
def compose_graticule():
|
||||
print('\t<g transform="matrix(1,0,0,-1,180,90)">')
|
||||
print('\t\t<g class="graticule">')
|
||||
generate_graticule(5, 1, include_tropics=True, adjust_poles=True)
|
||||
print('\t\t</g>')
|
||||
print('\t</g>')
|
||||
|
||||
def compose_graticule2():
|
||||
print('\t<g transform="matrix(1,0,0,-1,180,90)">')
|
||||
print('\t\t<g class="graticule">')
|
||||
generate_graticule(15, .25, include_tropics=True, adjust_poles=True, double_dateline=True)
|
||||
print('\t\t</g>')
|
||||
print('\t</g>')
|
||||
|
||||
def compose_compound():
|
||||
print('\t<g transform="matrix(1,0,0,-1,180,90)">')
|
||||
print('\t\t<g class="land">')
|
||||
generate_land('ne_50m', trim_antarctica=True)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="river">')
|
||||
generate_rivers('ne_50m', max_rank=4)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="lakes">')
|
||||
generate_lakes('ne_50m', max_rank=4)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="graticule">')
|
||||
generate_graticule(15, 1, include_tropics=True, adjust_poles=True)
|
||||
print('\t\t</g>')
|
||||
print('\t</g>')
|
||||
|
||||
def compose_indicatrices():
|
||||
print('\t<g transform="matrix(1,0,0,-1,180,90)">')
|
||||
print('\t\t<g class="land">')
|
||||
generate_land('ne_50m', trim_antarctica=True)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="lakes">')
|
||||
generate_lakes('ne_50m', max_rank=4)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="tissot">')
|
||||
generate_indicatrices(15, math.radians(3.75), adjust_poles=True)
|
||||
print('\t\t</g>')
|
||||
print('\t</g>')
|
||||
|
||||
def compose_indicatrices2(ctr_meridian):
|
||||
print('\t<g transform="matrix(1,0,0,-1,180,90)">')
|
||||
print('\t\t<g class="land">')
|
||||
generate_land('ne_110m')
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="lakes">')
|
||||
generate_lakes('ne_110m')
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="graticule">')
|
||||
generate_graticule(10, 1, double_dateline=True)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="tissot">')
|
||||
generate_indicatrices(30, 600/6371, ctr_meridian=ctr_meridian, adjust_poles=True)
|
||||
print('\t\t</g>')
|
||||
print('\t</g>')
|
||||
|
||||
def compose_political():
|
||||
print('\t<g transform="matrix(1,0,0,-1,180,90)">')
|
||||
print('\t\t<g class="country">')
|
||||
generate_borders('ne_50m', trim_antarctica=True)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="lakes">')
|
||||
generate_lakes('ne_50m', max_rank=4)
|
||||
print('\t\t</g>')
|
||||
print('\t</g>')
|
||||
|
||||
def compose_orthodromes():
|
||||
print('\t<g transform="matrix(1,0,0,-1,180,90)">')
|
||||
print('\t\t<g class="lines">')
|
||||
generate_orthodromes()
|
||||
print('\t\t</g>')
|
||||
print('\t</g>')
|
||||
|
||||
def compose_everything():
|
||||
print('\t<g transform="matrix(1,0,0,-1,180,90)">')
|
||||
print('\t\t<g class="country">')
|
||||
generate_borders('ne_50m', trim_antarctica=True, borders_only=False)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="sovereign">')
|
||||
generate_fine_borders('ne_50m')
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="dispute">')
|
||||
generate_disputes('ne_50m')
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="adminis">')
|
||||
generate_administrivia('ne_50m')
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="land">')
|
||||
generate_land('ne_50m', trim_antarctica=True)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="river">')
|
||||
generate_rivers('ne_50m')
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="lakes">')
|
||||
generate_lakes('ne_50m')
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="border">')
|
||||
generate_borders('ne_50m', trim_antarctica=True, borders_only=True)
|
||||
print('\t\t</g>')
|
||||
print('\t\t<g class="graticule">')
|
||||
generate_graticule(5, 1, include_tropics=True, adjust_poles=True)
|
||||
print('\t\t</g>')
|
||||
print('\t</g>')
|
||||
generate_topographical_labels('ne_50m')
|
||||
generate_political_labels('ne_50m')
|
||||
generate_urban_labels('ne_50m')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# compose_landmasses()
|
||||
# compose_graticule()
|
||||
# compose_compound()
|
||||
# compose_indicatrices()
|
||||
# compose_indicatrices2(-20)
|
||||
# compose_political()
|
||||
# compose_orthodromes()
|
||||
compose_everything()
|
||||
50
src/zupplemental/generate_borders.py
Normal file
@ -0,0 +1,50 @@
|
||||
import shapefile
|
||||
|
||||
from helpers import plot, trim_edges
|
||||
|
||||
|
||||
SIZE_CLASSES = ['lg', 'md', 'sm', None, None, None]
|
||||
|
||||
|
||||
def generate_borders(source, borders_only=False, labels=False, self_clip=False, trim_antarctica=False):
|
||||
"""data from http://www.naturalearthdata.com/"""
|
||||
sf = shapefile.Reader("data/{}_admin_0_countries".format(source))
|
||||
sovereigns = {} #key is 3-char code, value is list of (record,shape)
|
||||
for record, shape in zip(sf.records(), sf.shapes()):
|
||||
if trim_antarctica:
|
||||
if record[4] == 'ATA': #if it is Antarctica
|
||||
shape.points = trim_edges(shape.points)
|
||||
|
||||
sovereigns[record[4]] = sovereigns.get(record[4], []) + [(record, shape)]
|
||||
|
||||
for code in sorted(sovereigns.keys()):
|
||||
for record, shape in sovereigns[code]:
|
||||
if record[3] == record[8]:
|
||||
country_code = record[12]
|
||||
# metropole = (record, shape)
|
||||
# sovereigns[code].remove(metropole) #move the metropole to the front
|
||||
# sovereigns[code] = [metropole] + sovereigns[code]
|
||||
# break
|
||||
print('\t\t\t<g id="{}">'.format(country_code))
|
||||
for record, shape in sovereigns[code]:
|
||||
x1, y1, x2, y2 = shape.bbox
|
||||
x = (x1+x2)/2
|
||||
y = (y1+y2)/2
|
||||
region_code = record[12]
|
||||
if country_code == region_code: #metropole
|
||||
# name = record[3]
|
||||
text_size = SIZE_CLASSES[int(record[2]-2)]
|
||||
else: #dependency
|
||||
# name = '{} ({})'.format(record[8], record[3])
|
||||
text_size = SIZE_CLASSES[int(record[2]-1)]
|
||||
# if labels:
|
||||
# if text_size is not None:
|
||||
# print('\t\t\t\t<text class="label-{}" x="{:.3f}" y="{:.3f}">{}</text>'.format(text_size, x, y, name))
|
||||
if borders_only:
|
||||
print('\t\t\t\t<clipPath id="{0}-clipPath">'.format(region_code))
|
||||
print('\t\t\t\t\t<use href="#{0}-shape" />'.format(region_code))
|
||||
print('\t\t\t\t</clipPath>')
|
||||
print('\t\t\t\t<use href="#{0}-shape" style="fill:none; clip-path:url(#{0}-clipPath);" />'.format(region_code))
|
||||
else:
|
||||
plot(shape.points, midx=shape.parts, close=False, fourmat='xd', tabs=4, ident="{0}-shape".format(region_code))
|
||||
print('\t\t\t</g>')
|
||||
@ -6,72 +6,17 @@ from helpers import plot, is_widdershins
|
||||
|
||||
|
||||
SOURCE = 'ne_50m'
|
||||
SKIP = 1
|
||||
EXPAND_ANTARCTICA = True
|
||||
MAX_RANK = 2
|
||||
TRIM_ANTARCTICA = False
|
||||
MAX_RANK = float('inf')
|
||||
|
||||
|
||||
"""data from http://www.naturalearthdata.com/"""
|
||||
sf = shapefile.Reader("data/{}_coastline".format(SOURCE))
|
||||
pending_coasts = {} #key is last endpoint, value is list of points; every point should appear exactly twice, in each direction
|
||||
split_points = {} #key is first endpoint of master curve, value is list of indices where the curve must be split
|
||||
final_coasts = []
|
||||
sf = shapefile.Reader("data/{}_land".format(SOURCE))
|
||||
for record, shape in zip(sf.records(), sf.shapes()):
|
||||
curve = shape.points[0:len(shape.points):SKIP]
|
||||
if curve[-1] != shape.points[-1]: curve.append(shape.points[-1])
|
||||
if len(curve) < 4: continue
|
||||
if float(record[2]) > MAX_RANK: continue
|
||||
|
||||
if curve[0] == curve[-1]: #this curve is complete; we're done here
|
||||
final_coasts.append(curve)
|
||||
else:
|
||||
if curve[0] in pending_coasts: #if this is an extension of another path
|
||||
curve = pending_coasts.pop(curve[0]) + curve[1:] #replace that
|
||||
elif curve[-1] in pending_coasts: #if this reversed is an extension of another path
|
||||
curve = pending_coasts.pop(curve[-1]) + list(reversed(curve[:-1])) #replace that
|
||||
pending_coasts[curve[-1]] = curve
|
||||
pending_coasts[curve[0]] = list(reversed(curve))
|
||||
|
||||
# for key in pending_coasts:
|
||||
# assert pending_coasts[key][-1] == key, "The curve from {} to {} is keyed by {}".format(val[0], val[-1], key)
|
||||
# assert pending_coasts[key][0] in pending_coasts, "{} keys to a curve from {} to {}, but there is no key {}".format(key, val[0], val[-1], val[0])
|
||||
|
||||
for key, coast in pending_coasts.items():
|
||||
if pending_coasts[coast[0]] is not None: #if the mirror is not checked
|
||||
final_coasts.append(coast if is_widdershins(coast) else pending_coasts[coast[0]])
|
||||
pending_coasts[key] = None #mark this as checked
|
||||
|
||||
final_coasts = sorted(sorted(sorted(final_coasts, key=lambda c:c[0][0]), key=lambda c:c[0][1]), key=len, reverse=True)
|
||||
if SOURCE == 'ne_10m':
|
||||
afroeurasia = final_coasts[1]
|
||||
split_points[afroeurasia[0]] = [len(afroeurasia)]
|
||||
final_coasts[1] += final_coasts.pop(12) #manually attach the Caspian to Afroeurasia
|
||||
else:
|
||||
afroeurasia = final_coasts[0]
|
||||
for i in range(1,len(afroeurasia)):
|
||||
if math.hypot(afroeurasia[i][0]-afroeurasia[i-1][0], afroeurasia[i][1]-afroeurasia[i-1][1]) > 10:
|
||||
split_points[afroeurasia[0]] = [i] #manually detach the Caspian from Afroeurasia
|
||||
break
|
||||
if EXPAND_ANTARCTICA:
|
||||
antarctica = final_coasts[2]
|
||||
final_coasts[2] = [(antarctica[0][0], y) for y in range(-90, math.ceil(antarctica[0][1]))] +\
|
||||
antarctica + [(antarctica[-1][0], y) for y in range(math.floor(antarctica[-1][1]), -91, -1)]
|
||||
|
||||
sf = shapefile.Reader("data/{}_lakes".format(SOURCE))
|
||||
for record, lake in zip(sf.records(), sf.shapes()):
|
||||
if float(record[-2]) > MAX_RANK: #if this lake isn't important enough
|
||||
continue #skip it
|
||||
|
||||
x1l, y1l, x2l, y2l = lake.bbox
|
||||
for i, continent in enumerate(final_coasts):
|
||||
xc, yc = zip(*continent)
|
||||
x1c, y1c, x2c, y2c = min(xc), min(yc), max(xc), max(yc)
|
||||
if x1c < x1l and y1c < y1l and x2c > x2l and y2c > y2l: #if this continent contains this lake
|
||||
curve = lake.points[0:len(lake.points):SKIP]
|
||||
if curve[-1] != lake.points[-1]: curve.append(lake.points[-1])
|
||||
split_points[continent[0]] = split_points.get(continent[0], []) + [len(continent)+p for p in lake.parts] #mark the movetos
|
||||
final_coasts[i] = continent + curve #add it
|
||||
break
|
||||
|
||||
for coast in final_coasts:
|
||||
midx = [0] + split_points.get(coast[0], [])
|
||||
plot(coast, midx=midx, fourmat='xd', close=False)
|
||||
if TRIM_ANTARCTICA:
|
||||
if shape.points[0][0] == -180 and shape.points[0][1] < -80: #if it is Antarctica
|
||||
coast = shape.points
|
||||
shape.points = [(coast[0][0],y) for y in range(-90,int(coast[0][1]))] + coast + [(coast[-1][0],y) for y in range(int(coast[-1][1]),-91,-1)]
|
||||
plot(shape.points, midx=shape.parts, close=False, fourmat='xd')
|
||||
|
||||
@ -1,34 +1,30 @@
|
||||
import math
|
||||
|
||||
|
||||
GRATICULE = 15
|
||||
INCLUDE_TROPICS = True
|
||||
ADJUST_POLES = True #set to True to reduce the number of meridians as you approach the pole
|
||||
GRANULARITY = .25
|
||||
|
||||
AXIAL_TILT = 23.43694
|
||||
ANTI_TILT = 66.56306
|
||||
ANTI_TILT = 66.56306 #I have both of these because it's the easiest way to deal with roundoff
|
||||
|
||||
|
||||
def plot_meridian(lamd, cut=0, clazz=None):
|
||||
def plot_meridian(lamd, granularity, cut=0, clazz=None):
|
||||
class_attr = 'class="{}" '.format(clazz) if clazz is not None else ''
|
||||
tag = '\t\t\t<path {}d="M{},{}'.format(class_attr, lamd, cut-90) + 'v{}'.format(GRANULARITY)*round((180-2*cut)/GRANULARITY) + '" />'
|
||||
tag = '\t\t\t<path {}d="M{},{}'.format(class_attr, lamd, cut-90) + 'v{}'.format(granularity)*round((180-2*cut)/granularity) + '" />'
|
||||
print(tag)
|
||||
|
||||
|
||||
def plot_parallel(phid, cut=0, clazz=None):
|
||||
def plot_parallel(phid, granularity, cut=0, clazz=None):
|
||||
class_attr = 'class="{}" '.format(clazz) if clazz is not None else ''
|
||||
tag = '\t\t\t<path {}d="M{},{}'.format(class_attr, cut-180, phid) + 'h{}'.format(GRANULARITY)*round((360-2*cut)/GRANULARITY) + '" />'
|
||||
tag = '\t\t\t<path {}d="M{},{}'.format(class_attr, cut-180, phid) + 'h{}'.format(granularity)*round((360-2*cut)/granularity) + '" />'
|
||||
print(tag)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NUM_BASE = 90//GRATICULE
|
||||
def generate_graticule(spacing, granularity, include_tropics=False, adjust_poles=False, double_dateline=False):
|
||||
"""Generate a mesh of latitude and longitude lines"""
|
||||
NUM_BASE = 90//spacing
|
||||
cuts = [0]*(4*NUM_BASE)
|
||||
if ADJUST_POLES:
|
||||
if adjust_poles: #if this is True, reduce the number of meridians as you approach the pole
|
||||
old_num = 1
|
||||
for p in range(0, 90, GRATICULE):
|
||||
new_num = old_num*int(1/math.cos(math.radians(p+GRATICULE/2))/old_num)
|
||||
for p in range(0, 90, spacing):
|
||||
new_num = old_num*int(1/math.cos(math.radians(p+spacing/2))/old_num)
|
||||
while NUM_BASE%new_num != 0: new_num -= 1
|
||||
if new_num >= 2*old_num:
|
||||
for i in range(len(cuts)):
|
||||
@ -36,17 +32,17 @@ if __name__ == '__main__':
|
||||
cuts[i] = 90-p
|
||||
old_num = new_num
|
||||
|
||||
for x in range(GRATICULE, 180, GRATICULE):
|
||||
plot_meridian(-x, cut=cuts[x//GRATICULE%len(cuts)])
|
||||
plot_meridian(x, cut=cuts[x//GRATICULE%len(cuts)])
|
||||
for y in range(GRATICULE, 90, GRATICULE):
|
||||
plot_parallel(-y)
|
||||
plot_parallel(y)
|
||||
for x in [-180, 0, 180]:
|
||||
plot_meridian(x, clazz="prime-m")
|
||||
plot_parallel(0, clazz="equator")
|
||||
if INCLUDE_TROPICS:
|
||||
plot_parallel(-AXIAL_TILT, clazz="tropics")
|
||||
plot_parallel(AXIAL_TILT, clazz="tropics")
|
||||
plot_parallel(-ANTI_TILT, clazz="circles")
|
||||
plot_parallel(ANTI_TILT, clazz="circles")
|
||||
for x in range(spacing, 180, spacing):
|
||||
plot_meridian(-x, granularity, cut=cuts[x//spacing%len(cuts)])
|
||||
plot_meridian(x, granularity, cut=cuts[x//spacing%len(cuts)])
|
||||
for y in range(spacing, 90, spacing):
|
||||
plot_parallel(-y, granularity)
|
||||
plot_parallel(y, granularity)
|
||||
for x in [-180, 0, 180] if double_dateline else [-180, 0]:
|
||||
plot_meridian(x, granularity, clazz="prime-m")
|
||||
plot_parallel(0, granularity, clazz="equator")
|
||||
if include_tropics:
|
||||
plot_parallel(-AXIAL_TILT, granularity, clazz="tropics")
|
||||
plot_parallel(AXIAL_TILT, granularity, clazz="tropics")
|
||||
plot_parallel(-ANTI_TILT, granularity, clazz="circles")
|
||||
plot_parallel(ANTI_TILT, granularity, clazz="circles")
|
||||
|
||||
@ -3,55 +3,53 @@ import math
|
||||
from helpers import obliquify, plot
|
||||
|
||||
|
||||
IND_NUM = 360
|
||||
# IND_RAD = math.radians(3.75)
|
||||
IND_RAD = 750/6371
|
||||
SPACING = 30
|
||||
ADJUST_SPACING = True
|
||||
# IND_RAD = 750/6371
|
||||
|
||||
|
||||
def plot_indicatrix(phi0, lam0, r):
|
||||
def plot_indicatrix(phi0, lam0, r, res):
|
||||
points = []
|
||||
for l in range(0, 360, 360//IND_NUM):
|
||||
for l in range(0, 360, 360//res):
|
||||
points.append(obliquify(math.pi/2-r, math.radians(l), phi0, lam0))
|
||||
plot(points)
|
||||
|
||||
|
||||
def plot_side_indicatrix(phi0, r):
|
||||
def plot_side_indicatrix(phi0, r, res):
|
||||
points = []
|
||||
midx = [0]
|
||||
for l in range(0, 181, 360//IND_NUM):
|
||||
points.append(obliquify(math.pi/2-r, math.radians(l), phi0, -math.pi))
|
||||
for l in range(0, 181, 360//res):
|
||||
pr, lr = obliquify(math.pi/2-r, math.radians(l), phi0, 0)
|
||||
points.append((pr, lr - math.pi))
|
||||
midx.append(len(points))
|
||||
for l in range(180, 361, 360//IND_NUM):
|
||||
pr, lr = obliquify(math.pi/2-r, math.radians(l), phi0, -math.pi)
|
||||
points.append((pr, lr+2*math.pi))
|
||||
for l in range(180, 361, 360//res):
|
||||
pr, lr = obliquify(math.pi/2-r, math.radians(l), phi0, 0)
|
||||
points.append((pr, lr + math.pi))
|
||||
plot(points, midx=midx, close=False)
|
||||
|
||||
|
||||
def plot_pole_indicatrix(north, r):
|
||||
def plot_pole_indicatrix(north, r, res):
|
||||
if north:
|
||||
pp, pr = math.pi/2, math.pi/2-r
|
||||
else:
|
||||
pp, pr = -math.pi/2, -math.pi/2+r
|
||||
|
||||
points = [(pp, -math.pi)]
|
||||
for x in range(-180, 181, 360//IND_NUM):
|
||||
for x in range(-180, 181, 360//res):
|
||||
points.append((pr, math.radians(x)))
|
||||
points.append((pp, math.pi))
|
||||
plot(points)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
plot_pole_indicatrix(True, IND_RAD)
|
||||
for y in range(-90+SPACING, 90, SPACING):
|
||||
if ADJUST_SPACING:
|
||||
step = SPACING * round(1/math.cos(math.radians(y)))
|
||||
def generate_indicatrices(spacing, radius, adjust_poles=False, resolution=60, ctr_meridian=0):
|
||||
plot_pole_indicatrix(True, radius, resolution)
|
||||
for y in range(-90+spacing, 90, spacing):
|
||||
if adjust_poles:
|
||||
step = spacing * round(1/math.cos(math.radians(y)))
|
||||
else:
|
||||
step = SPACING
|
||||
step = spacing
|
||||
for x in range(-180, 180, step):
|
||||
if abs(x) == 180:
|
||||
plot_side_indicatrix(math.radians(y), math.pi/36)
|
||||
if abs(x+ctr_meridian) == 180:
|
||||
plot_side_indicatrix(math.radians(y), radius, resolution)
|
||||
else:
|
||||
plot_indicatrix(math.radians(y), math.radians(x), IND_RAD)
|
||||
plot_pole_indicatrix(False, IND_RAD)
|
||||
plot_indicatrix(math.radians(y), math.radians(x+ctr_meridian), radius, resolution)
|
||||
plot_pole_indicatrix(False, radius, resolution)
|
||||
|
||||
58
src/zupplemental/generate_labels.py
Normal file
@ -0,0 +1,58 @@
|
||||
#read data from a shapefile into SVG format
|
||||
|
||||
import shapefile
|
||||
import math
|
||||
|
||||
from helpers import line_break, get_centroid
|
||||
|
||||
|
||||
def plot_texts(data, label_class, source, max_rank, regulate_case=False, secondary_attr=None, force_points=False):
|
||||
"""data from http://www.naturalearthdata.com/"""
|
||||
sf = shapefile.Reader("data/{}_{}".format(source, data))
|
||||
lat_idx = None
|
||||
for i, field in enumerate(sf.fields):
|
||||
if field[0] in ['scalerank', 'LABELRANK']:
|
||||
rank_idx = i-1
|
||||
if field[0] in ['name', 'NAME']:
|
||||
name_idx = i-1
|
||||
if field[0] == 'featurecla':
|
||||
type_idx = i-1
|
||||
if field[0] == secondary_attr:
|
||||
secd_idx = i-1
|
||||
if field[0] == 'lat_y':
|
||||
lat_idx = i-1
|
||||
if field[0] == 'long_x':
|
||||
lon_idx = i-1
|
||||
for record, shape in zip(sf.records(), sf.shapes()):
|
||||
rank = record[rank_idx]
|
||||
if rank <= max_rank:
|
||||
if lat_idx is not None:
|
||||
x, y = record[lon_idx], record[lat_idx]
|
||||
else:
|
||||
x, y = get_centroid(shape.points, shape.parts)
|
||||
label = record[name_idx]
|
||||
if not label:
|
||||
continue #we can't waste our time worrying about features with no names
|
||||
if label == 'EUROPE':
|
||||
rank += 1 #You're not a continent! Get over it!
|
||||
if regulate_case:
|
||||
label = label.upper()
|
||||
if secondary_attr is not None and record[7] == 'Dependency':
|
||||
label = "{} ({})".format(label, record[secd_idx]) #indicate dependencies
|
||||
text_size = ['sm', 'md', 'lg', 'xl'][min(3, int(max_rank-rank))] #lower ranks get bigger text
|
||||
print('\t<text class="label-{} label-{}" x="{:.03f}" y="{:.03f}">{}</text>'.format(
|
||||
label_class, text_size, 180+x, 90-y, label))
|
||||
if force_points or record[type_idx] in ['mountain', 'depression', 'pole', 'waterfall']: #add circles to the ones that need markers
|
||||
print('\t<circle cx="{:.03f}" cy="{:.03f}" r="{:.03f}" />'.format(180+x, 90-y, math.sqrt(max_rank-rank+1)*0.15))
|
||||
|
||||
def generate_political_labels(source, max_rank=4):
|
||||
plot_texts('admin_0_countries', 'pol', source, max_rank, secondary_attr='NOTE_ADM0')
|
||||
|
||||
def generate_topographical_labels(source, max_rank=2):
|
||||
plot_texts('geography_regions_points', 'geo', source, max_rank)
|
||||
plot_texts('geography_regions_polys', 'geo', source, max_rank, regulate_case=True)
|
||||
plot_texts('geography_regions_elevation_points', 'geo', source, max_rank)
|
||||
plot_texts('geography_marine_polys', 'sea', source, max_rank)
|
||||
|
||||
def generate_urban_labels(source, max_rank=1):
|
||||
plot_texts('populated_places', 'pol', source, max_rank, force_points=True)
|
||||
29
src/zupplemental/generate_lakes.py
Normal file
@ -0,0 +1,29 @@
|
||||
import math
|
||||
import numpy as np
|
||||
import shapefile
|
||||
|
||||
from helpers import plot
|
||||
|
||||
|
||||
SOURCE = 'ne_50m'
|
||||
SKIP = 1
|
||||
MAX_RANK = 2
|
||||
INCLUDE_LAKES = False
|
||||
|
||||
|
||||
"""data from http://www.naturalearthdata.com/"""
|
||||
sf = shapefile.Reader("data/{}_lakes".format(SOURCE))
|
||||
for record, lake in zip(sf.records(), sf.shapes()):
|
||||
if float(record[-2]) > MAX_RANK: #if this lake isn't important enough
|
||||
continue #skip it
|
||||
|
||||
x1l, y1l, x2l, y2l = lake.bbox
|
||||
for i, continent in enumerate(final_coasts):
|
||||
xc, yc = zip(*continent)
|
||||
x1c, y1c, x2c, y2c = min(xc), min(yc), max(xc), max(yc)
|
||||
if x1c < x1l and y1c < y1l and x2c > x2l and y2c > y2l: #if this continent contains this lake
|
||||
curve = lake.points[0:len(lake.points):SKIP]
|
||||
if curve[-1] != lake.points[-1]: curve.append(lake.points[-1])
|
||||
split_points[continent[0]] = split_points.get(continent[0], []) + [len(continent)+p for p in lake.parts] #mark the movetos
|
||||
final_coasts[i] = continent + curve #add it
|
||||
break
|
||||
@ -14,7 +14,8 @@ def plot_orthodrome(phi0, lam0, tht0):
|
||||
plot(points, close=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def generate_orthodromes():
|
||||
"""generate an icosohedral orthodromic mesh, like the Brilliant logo (#notsponsored)"""
|
||||
for l in range(-180, 180, 36):
|
||||
plot_orthodrome(math.pi/2, 0, math.radians(l))
|
||||
for l in range(0, 360, 72):
|
||||
|
||||
38
src/zupplemental/generate_shape.py
Normal file
@ -0,0 +1,38 @@
|
||||
#read data from a shapefile into SVG format
|
||||
|
||||
import shapefile
|
||||
|
||||
from helpers import plot, trim_edges
|
||||
|
||||
|
||||
def plot_shape(data, source, max_rank, trim_antarctica=False):
|
||||
"""data from http://www.naturalearthdata.com/"""
|
||||
sf = shapefile.Reader("data/{}_{}".format(source, data))
|
||||
for i, field in enumerate(sf.fields):
|
||||
if 'rank' in field[0]:
|
||||
rank_idx = i-1
|
||||
for record, shape in zip(sf.records(), sf.shapes()):
|
||||
if record[rank_idx] <= max_rank:
|
||||
if trim_antarctica:
|
||||
if shape.points[0][1] < -60: #if it is Antarctica (this will have a few false positives, but that's fine)
|
||||
shape.points = trim_edges(shape.points)
|
||||
|
||||
plot(shape.points, midx=shape.parts, close=False, fourmat='xd')
|
||||
|
||||
def generate_fine_borders(source, max_rank=float('inf')):
|
||||
plot_shape('admin_0_map_units', source, max_rank)
|
||||
|
||||
def generate_disputes(source, max_rank=float('inf')):
|
||||
plot_shape('admin_0_boundary_lines_disputed_areas', source, max_rank)
|
||||
|
||||
def generate_administrivia(source, max_rank=float('inf')):
|
||||
plot_shape('admin_1_states_provinces_lines', source, max_rank)
|
||||
|
||||
def generate_land(source, max_rank=float('inf'), trim_antarctica=False):
|
||||
plot_shape('land', source, max_rank, trim_antarctica=trim_antarctica)
|
||||
|
||||
def generate_rivers(source, max_rank=float('inf')):
|
||||
plot_shape('rivers_lake_centerlines', source, max_rank)
|
||||
|
||||
def generate_lakes(source, max_rank=float('inf')):
|
||||
plot_shape('lakes', source, max_rank)
|
||||
@ -1,4 +1,5 @@
|
||||
import math
|
||||
import random as rng
|
||||
|
||||
|
||||
def obliquify(lat1, lon1, lat0, lon0):
|
||||
@ -21,14 +22,20 @@ def obliquify(lat1, lon1, lat0, lon0):
|
||||
|
||||
while lonf > math.pi:
|
||||
lonf -= 2*math.pi
|
||||
while lonf < -math.pi:
|
||||
lonf += 2*math.pi
|
||||
|
||||
return latf, lonf
|
||||
|
||||
|
||||
def plot(coords, midx=[0], close=True, fourmat='pr'):
|
||||
tag = '\t\t\t<path d="'
|
||||
def plot(coords, midx=[0], close=True, fourmat='pr', clazz=None, ident=None, tabs=3):
|
||||
class_attr = 'class="{}" '.format(clazz) if clazz is not None else ''
|
||||
ident_attr = 'id="{}" '.format(ident) if ident is not None else ''
|
||||
tag = '\t'*tabs+'<path {}{}d="'.format(class_attr, ident_attr)
|
||||
last_move = None
|
||||
for i, coord in enumerate(coords):
|
||||
if i > 0 and coords[i-1] == coords[i]:
|
||||
continue #no point repeating commands, though that sometimes happens
|
||||
if coord == last_move:
|
||||
tag += 'Z'
|
||||
continue #save space by removing unnecessary repetitions
|
||||
@ -51,9 +58,76 @@ def plot(coords, midx=[0], close=True, fourmat='pr'):
|
||||
print(tag.replace('.000',''))
|
||||
|
||||
|
||||
def is_widdershins(coords): #this method is not exact, but it should work well enough for my purposes.
|
||||
x1, y1 = coords[0]
|
||||
x2, y2 = coords[len(coords)//4]
|
||||
x3, y3 = coords[len(coords)*3//4]
|
||||
def trim_edges(coast):
|
||||
"""remove the extra points placed along the edges of the Plate Carree map"""
|
||||
for i in range(len(coast)-1, -1, -1):
|
||||
x0, y0 = coast[i-1] if i > 0 else coast[i]
|
||||
x1, y1 = coast[i]
|
||||
x2, y2 = coast[i+1] if i < len(coast)-1 else coast[i]
|
||||
if (abs(x0) > 179.99 or abs(y0) > 89.99) and (abs(x1) > 179.99 or abs(y1) > 89.99) and (abs(x2) > 179.99 or abs(y2) > 89.99):
|
||||
coast.pop(i)
|
||||
return coast
|
||||
|
||||
return (x2-x1)*(y3-y1) - (x3-x1)*(y2-y1) > 0
|
||||
|
||||
def line_break(line):
|
||||
"""replace some space with a newline; whichever space is closest to the center"""
|
||||
total_length = len(line)
|
||||
words = line.split(' ')
|
||||
best_length = float('inf')
|
||||
left_length = 0
|
||||
for i in range(len(words)-1):
|
||||
left_length += len(words[i])+1
|
||||
if max(left_length, total_length-left_length) < best_length:
|
||||
best_length = max(left_length, total_length-left_length)
|
||||
best_idx = left_length
|
||||
return line[:best_idx-1] + '\n' + line[best_idx:]
|
||||
|
||||
|
||||
def get_centroid(points, parts=None):
|
||||
"""Compute the centroid of the spherical shape"""
|
||||
minL = min([p[0] for p in points])
|
||||
minP = min([p[1] for p in points])
|
||||
maxL = max([p[0] for p in points])
|
||||
maxP = max([p[1] for p in points])
|
||||
|
||||
if maxL-minL < 90:
|
||||
return ((maxL+minL)/2, (maxP+minP)/2)
|
||||
elif parts: #if there are multiple parts, try guessing the centroid of just one; the biggest one by bounding box
|
||||
parts.append(len(points))
|
||||
max_area = 0
|
||||
for i in range(1, len(parts)):
|
||||
part = points[parts[i-1]:parts[i]]
|
||||
minL = min([p[0] for p in part])
|
||||
minP = min([p[1] for p in part])
|
||||
maxL = max([p[0] for p in part])
|
||||
maxP = max([p[1] for p in part])
|
||||
if (maxL-minL)*(maxP-minP) > max_area:
|
||||
max_area = (maxL-minL)*(maxP-minP)
|
||||
best_part = (parts[i-1], parts[i])
|
||||
return get_centroid(points[best_part[0]:best_part[1]])
|
||||
|
||||
lines = []
|
||||
for i in range(1, len(points)):
|
||||
lines += [(*points[i-1], *points[i])]
|
||||
xc, yc, zc = 0, 0, 0
|
||||
j = 0
|
||||
num_in = 0
|
||||
while j < 4000 or num_in < 10:
|
||||
j += 1
|
||||
latr = math.asin(rng.random()*2-1)
|
||||
lonr = rng.random()*2*math.pi - math.pi
|
||||
latd, lond = math.degrees(latr), math.degrees(lonr)
|
||||
num_crosses = 0
|
||||
for l1, p1, l2, p2 in lines: #count the lines a northward ray crosses
|
||||
if ((l1 <= lond and l2 > lond) or (l2 <= lond and l1 > lond)) and (p1*(l2-lond)/(l2-l1) + p2*(l1-lond)/(l1-l2) > latd):
|
||||
num_crosses += 1
|
||||
|
||||
if num_crosses%2 == 1: #if odd,
|
||||
num_in += 1
|
||||
xc += math.cos(latr)*math.cos(lonr) #this point is in.
|
||||
yc += math.cos(latr)*math.sin(lonr) #update the centroid
|
||||
zc += math.sin(latr)
|
||||
|
||||
loncr = math.atan2(yc, xc)
|
||||
latcr = math.atan2(zc, math.hypot(xc, yc))
|
||||
return (math.degrees(loncr), math.degrees(latcr))
|
||||
|
||||