diff --git a/.vscode/settings.json b/.vscode/settings.json
index ead524ee..a46e5391 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,6 @@
{
"minify.minifyExistingOnSave": true,
- "editor.snippetSuggestions": "top"
+ "editor.snippetSuggestions": "top",
+ "python.pythonPath": "C:\\Users\\hausss\\.pyenv\\pyenv-win\\versions\\3.8.2\\python.exe",
+ "python.formatting.provider": "black"
}
\ No newline at end of file
diff --git a/media/attribution.js b/media/attribution.js
index e9131927..32ecf2f9 100644
--- a/media/attribution.js
+++ b/media/attribution.js
@@ -12136,6 +12136,13 @@ const attributionData = {
"cdc"
]
},
+ {
+ prefix: "./media/naturals/global-cities",
+ all: "https://openstreetmap.org",
+ authors: [
+ "openstreetmap"
+ ]
+ },
{
prefix: "./media/food/plants/",
files: [
diff --git a/media/naturals/global-cities/Beijing.svg b/media/naturals/global-cities/Beijing.svg
new file mode 100644
index 00000000..ce067b9d
--- /dev/null
+++ b/media/naturals/global-cities/Beijing.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/Berlin.svg b/media/naturals/global-cities/Berlin.svg
new file mode 100644
index 00000000..8011eddb
--- /dev/null
+++ b/media/naturals/global-cities/Berlin.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/Hong Kong.svg b/media/naturals/global-cities/Hong Kong.svg
new file mode 100644
index 00000000..3283b2ca
--- /dev/null
+++ b/media/naturals/global-cities/Hong Kong.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/Houston.svg b/media/naturals/global-cities/Houston.svg
new file mode 100644
index 00000000..e4424bd8
--- /dev/null
+++ b/media/naturals/global-cities/Houston.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/London.svg b/media/naturals/global-cities/London.svg
new file mode 100644
index 00000000..e337ebeb
--- /dev/null
+++ b/media/naturals/global-cities/London.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/Madrid.svg b/media/naturals/global-cities/Madrid.svg
new file mode 100644
index 00000000..f5c1498f
--- /dev/null
+++ b/media/naturals/global-cities/Madrid.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/Melbourne.svg b/media/naturals/global-cities/Melbourne.svg
new file mode 100644
index 00000000..58ecb0a3
--- /dev/null
+++ b/media/naturals/global-cities/Melbourne.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/Moscow.svg b/media/naturals/global-cities/Moscow.svg
new file mode 100644
index 00000000..6b5d6067
--- /dev/null
+++ b/media/naturals/global-cities/Moscow.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/New York City.svg b/media/naturals/global-cities/New York City.svg
new file mode 100644
index 00000000..4d951ea5
--- /dev/null
+++ b/media/naturals/global-cities/New York City.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/Paris.svg b/media/naturals/global-cities/Paris.svg
new file mode 100644
index 00000000..d5de2a87
--- /dev/null
+++ b/media/naturals/global-cities/Paris.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/Seoul.svg b/media/naturals/global-cities/Seoul.svg
new file mode 100644
index 00000000..3232b7f8
--- /dev/null
+++ b/media/naturals/global-cities/Seoul.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/media/naturals/global-cities/Sydney.svg b/media/naturals/global-cities/Sydney.svg
new file mode 100644
index 00000000..1a59b925
--- /dev/null
+++ b/media/naturals/global-cities/Sydney.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/presets/naturals.js b/presets/naturals.js
index 6d7728d4..6b851fb2 100644
--- a/presets/naturals.js
+++ b/presets/naturals.js
@@ -881,6 +881,19 @@ const cityData = [["Perris, CA", 81964684.1824094, 17340.390703739322],
["Santa Barbara, CA", 50991347.46845491, 7484.009670940266],
["Rio Rancho, NM", 269136968.1901309, 23814.351777786604]];
+const globalCityData = [
+ ["London", 1595951001.243258, 45078.7351319594],
+ ["Paris", 105295835.99015382, 9638.711635935364],
+ ["Melbourne", 37645388.45504674, 8374.051389167866],
+ ["Sydney", 4324012960.9402275, 89922.41773568685],
+ ["Berlin", 889606455.0316621, 37503.65910136853],
+ ["Moscow", 2556449120.0771136, 97614.50005992019],
+ ["Seoul", 607651322.3035502, 30365.92222630693],
+ ["Madrid", 605538730.8845485, 36915.51233779266],
+ ["Hong Kong", 152936581.1183557, 11981.32777728141],
+ ["Beijing", 16426832679.115694, 178940.11723688344],
+]
+
function makePlanet(name, diameter, mass, image) {
return {
name: name,
@@ -928,7 +941,7 @@ function makeMountains() {
};
}
-function makeGIS(data, category) {
+function makeGIS(data, category, rename=true) {
const baseLength = math.unit(1, "meters");
const baseArea = math.unit(1, "meters^2");
return {
@@ -960,7 +973,7 @@ function makeGIS(data, category) {
name: name,
rename: true,
image: {
- source: "./media/naturals/" + category.toLowerCase().replace(/ /g, "-") + "/" + name.toLowerCase().replace(/ /g, "-") + ".svg"
+ source: "./media/naturals/" + category.toLowerCase().replace(/ /g, "-") + "/" + (rename ? name.toLowerCase().replace(/ /g, "-") + ".svg" : name + ".svg")
}
}
});
@@ -1087,6 +1100,13 @@ function makeNaturals() {
"Cities"
));
+ results.push(makeGIS(
+ globalCityData.sort((s1, s2) => {
+ return s1[0].localeCompare(s2[0])
+ }),
+ "Global Cities",
+ false
+ ));
results.push(makeSkylines(
[
["Los Angeles", math.unit(1018, "feet")],
diff --git a/scripts/osm-bounds/.gitignore b/scripts/osm-bounds/.gitignore
new file mode 100644
index 00000000..919463b9
--- /dev/null
+++ b/scripts/osm-bounds/.gitignore
@@ -0,0 +1,2 @@
+*.svg
+*.json
\ No newline at end of file
diff --git a/scripts/osm-bounds/cities.txt b/scripts/osm-bounds/cities.txt
new file mode 100644
index 00000000..358e04d5
--- /dev/null
+++ b/scripts/osm-bounds/cities.txt
@@ -0,0 +1,10 @@
+London
+Paris
+Melbourne
+Sydney
+Berlin
+Moscow
+Seoul
+Madrid
+Hong Kong
+Beijing
\ No newline at end of file
diff --git a/scripts/osm-bounds/get-polygon.py b/scripts/osm-bounds/get-polygon.py
new file mode 100644
index 00000000..b07a7ca7
--- /dev/null
+++ b/scripts/osm-bounds/get-polygon.py
@@ -0,0 +1,88 @@
+import requests
+import sys
+import urllib.parse
+import json
+import subprocess
+import os
+
+def get_polygon(name):
+ if not os.path.isfile(name + ".json"):
+ url = "https://nominatim.openstreetmap.org/search.php?q={0}&polygon_geojson=1&format=jsonv2".format(
+ urllib.parse.quote(name)
+ )
+ r = requests.get(url)
+ data = json.loads(r.text)
+
+ if data is None:
+ raise ValueError("Bogus results")
+
+ osm_id = None
+ for entry in data:
+ if "boundary" in entry["category"] and entry["osm_type"] == "relation":
+ osm_id = entry["osm_id"]
+ break
+
+ if not osm_id:
+ raise ValueError("No id")
+
+ url = "http://polygons.openstreetmap.fr/get_geojson.py?id={0}¶ms=0".format(osm_id)
+
+ with open(name + ".json", "w") as file:
+ file.write(requests.get(url).text)
+ else:
+ data = json.load(open(name + ".json"))
+
+ info = subprocess.check_output(
+ [
+ "mapshaper",
+ "-i",
+ "{0}.json".format(name),
+ "-each",
+ "console.log(this.centroidX, this.centroidY, this.area)"],
+ shell=True,
+ stderr=subprocess.STDOUT,
+ ).decode("utf-8")
+
+ lon, lat, area = list(map(float, info.split(" ")))
+ if area is not None:
+ info = subprocess.check_output(
+ [
+ "mapshaper",
+ "-i",
+ "{0}.json".format(name),
+ "-proj",
+ "+proj=nsper",
+ "+h=100000",
+ "+lat_0={0}".format(lat),
+ "+lon_0={0}".format(lon),
+ "-simplify",
+ "resolution=500x500",
+ "-each",
+ "console.log(\"HEIGHT:\" + this.height)",
+ "-o",
+ "{0}.svg".format(name),
+ ],
+ shell=True,
+ stderr=subprocess.STDOUT,
+ ).decode("utf-8")
+ height = None
+ for line in info.split("\n"):
+ if "HEIGHT:" in line:
+ height = float(line.split(":")[1].strip())
+ print("[\"{0}\", {1}, {2}],".format(name, area, height))
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ print("Usage: {0} list-of-cities".format(sys.argv[0]))
+ else:
+ city = None
+ try:
+ for city in open(sys.argv[1]).readlines():
+ try:
+ get_polygon(city.strip())
+ except ValueError as e:
+ print(city + " failed")
+ print(e)
+ except Exception as e:
+ print(city + " failed")
+ print(e)
\ No newline at end of file
diff --git a/scripts/osm-bounds/process-shapefile.py b/scripts/osm-bounds/process-shapefile.py
new file mode 100644
index 00000000..3473bf3c
--- /dev/null
+++ b/scripts/osm-bounds/process-shapefile.py
@@ -0,0 +1,41 @@
+import subprocess
+import sys
+import json
+
+def get_centroids(file, field_name):
+ results = {}
+ for entry in subprocess.check_output(
+ [
+ "mapshaper",
+ "-i",
+ file,
+ "-each",
+ "\"console.log(JSON.stringify({{name: {0}, lon: this.centroidX, lat: this.centroidY}}))\"".format(field_name)
+ ],
+ shell = True,
+ stderr = subprocess.STDOUT
+ ).decode("utf-8").split("\n"):
+ print(entry)
+ parts = json.loads(entry)
+ results[parts["name"]] = {
+ "lon": float(parts["lon"]),
+ "lat": float(parts["lat"])
+ }
+
+ return results
+
+def extract_shape(file, field_name, field_value):
+ lat, lon = subprocess.check_output(
+ [
+ "mapshaper",
+ "-i",
+ file,
+ "-filter",
+ "\"{0} == {1}\"".format(field_name, field_value),
+ "-each",
+ "\"console.log(this.centroidX, this.centroidY)"
+ ]
+ )
+
+if __name__ == "__main__":
+ print(get_centroids(sys.argv[1], sys.argv[2]))
\ No newline at end of file