working natural earth mapconverter
This commit is contained in:
parent
a580fbbba1
commit
5afa90b2dc
30
Map.cpp
30
Map.cpp
|
@ -4,14 +4,14 @@
|
||||||
|
|
||||||
bool Map::QTInsert(QuadTree *tree, Line *line, int depth) {
|
bool Map::QTInsert(QuadTree *tree, Line *line, int depth) {
|
||||||
|
|
||||||
if(depth > 25) {
|
// if(depth > 25) {
|
||||||
// printf("fail [%f %f] -> [%f %f]\n",line->start.lon,line->start.lat,line->end.lon,line->end.lat);
|
// // printf("fail [%f %f] -> [%f %f]\n",line->start.lon,line->start.lat,line->end.lon,line->end.lat);
|
||||||
|
|
||||||
// printf("bounds %f %f %f %f\n",tree->lon_min, tree->lon_max, tree->lat_min, tree->lat_max);
|
// // printf("bounds %f %f %f %f\n",tree->lon_min, tree->lon_max, tree->lat_min, tree->lat_max);
|
||||||
// fflush(stdout);
|
// // fflush(stdout);
|
||||||
tree->lines.push_back(&(*line));
|
// tree->lines.push_back(&(*line));
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
bool startInside = line->start.lat >= tree->lat_min &&
|
bool startInside = line->start.lat >= tree->lat_min &&
|
||||||
|
@ -24,17 +24,17 @@ bool Map::QTInsert(QuadTree *tree, Line *line, int depth) {
|
||||||
line->end.lon >= tree->lon_min &&
|
line->end.lon >= tree->lon_min &&
|
||||||
line->end.lon <= tree->lon_max;
|
line->end.lon <= tree->lon_max;
|
||||||
|
|
||||||
if (!startInside || !endInside) {
|
// if (!startInside || !endInside) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// if (!startInside && !endInside) {
|
|
||||||
// return false;
|
// return false;
|
||||||
// }
|
// }
|
||||||
|
if (!startInside && !endInside) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// if (startInside != endInside) {
|
if (startInside != endInside) {
|
||||||
// tree->lines.push_back(&(*line));
|
tree->lines.push_back(&(*line));
|
||||||
// return true;
|
return true;
|
||||||
// }
|
}
|
||||||
|
|
||||||
if (tree->nw == NULL) {
|
if (tree->nw == NULL) {
|
||||||
tree->nw = new QuadTree;
|
tree->nw = new QuadTree;
|
||||||
|
|
41
README.md
41
README.md
|
@ -1,12 +1,20 @@
|
||||||
# viz1090
|
# viz1090
|
||||||
|
|
||||||
**This is work in progress**
|
**This is work in progress**
|
||||||
There are a lot of missing pieces in this implementation so far:
|
|
||||||
* A proper map system yet. Eventually map data should be pulled from Mapbox or similar.
|
|
||||||
* In-application menus or configuration yet.
|
There are some major fixes and cleanup that need to happen before a relase:
|
||||||
|
* Everything is a grab bag of C and C++, need to more consistently modernize
|
||||||
|
* A full refactor, especially View.cpp, necessary for many of the new features below.
|
||||||
|
* A working Android build, as this is the best way to run this on portable hardware.
|
||||||
|
|
||||||
|
There are also a lot of missing features:
|
||||||
|
* Map improvements
|
||||||
|
* Labels, different colors/line weights for features
|
||||||
|
* Tile prerenderer for improved performance
|
||||||
|
* In-application menus for view options and configuration
|
||||||
* Theming/colormaps (important as this is primarily intended to be eye candy!)
|
* Theming/colormaps (important as this is primarily intended to be eye candy!)
|
||||||
* Integration with handheld features like GPS, battery monitors, buttons/dials, etc.
|
* Integration with handheld features like GPS, battery monitors, buttons/dials, etc.
|
||||||
* Android build is currently broken
|
|
||||||
|
|
||||||
### BUILDING
|
### BUILDING
|
||||||
|
|
||||||
|
@ -34,30 +42,21 @@ make clean; make
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Download and process map data
|
3. Download and process map data
|
||||||
Until more comprehensive map source (e.g., Mapbox) is integrated, viz1090 uses the lat/lon SVG files from https://www.mccurley.org
|
Grab a shapefile with your desired level of detail from https://www.naturalearthdata.com/downloads
|
||||||
|
|
||||||
The getmap.sh pulls the large SVG file for the contiguous 48 US states and produces a binary file for viz1090 to read.
|
[This](https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip) is a good place to start.
|
||||||
|
|
||||||
|
Unzip and copy the .shp and .shx files.
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo apt install python3 python3-pip
|
sudo apt install python3 python3-pip
|
||||||
pip3 install lxml numpy tqdm
|
pip3 install geopandas tqdm
|
||||||
./getmap.sh
|
python3 mapconverter.py ne_10m_admin_1_states_provinces.shp
|
||||||
```
|
```
|
||||||
|
|
||||||
There is also a world map avaiable from McCurley (https://mccurley.org/svg/data/World.svgz), which is much lower resolution and thus better for lower power hardware.
|
This will produce a file mapdata.bin that viz1090 reads. If the file doesn't exist then visualizer will show planes and trails without any geography.
|
||||||
|
|
||||||
The mapconverter script called by getmap.sh downsamples the file to render resonably quickly on a Raspberri Pi 4. If you are on a slower device (e.g, a Raspberry Pi 3), you may want to try something like:
|
The default parameters for mapconverter should render resonably quickly on a Raspberri Pi 4. See the mapconverter section below for other options.
|
||||||
|
|
||||||
```
|
|
||||||
python3 mapconverter.py --resolution 64 all.svg
|
|
||||||
```
|
|
||||||
|
|
||||||
On the other hand, if you are on a modern desktop or laptop, you can use something higher (but you probably don't need the full 6 digit precision of the McCurley SVG file):
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 mapconverter.py --resolution 8192 all.svg
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
3. (Windows only)
|
3. (Windows only)
|
||||||
|
|
39
View.cpp
39
View.cpp
|
@ -292,7 +292,7 @@ void View::font_init() {
|
||||||
// todo separate style stuff
|
// todo separate style stuff
|
||||||
//
|
//
|
||||||
|
|
||||||
SDL_Color bgcolor = {0,0,0,255};
|
SDL_Color bgcolor = {0,0,20,255};
|
||||||
SDL_Color greenblue = {236,192,68,255};
|
SDL_Color greenblue = {236,192,68,255};
|
||||||
SDL_Color lightblue = {211,208,203,255};
|
SDL_Color lightblue = {211,208,203,255};
|
||||||
SDL_Color mediumblue ={110,136,152,255};
|
SDL_Color mediumblue ={110,136,152,255};
|
||||||
|
@ -667,26 +667,33 @@ void View::drawLinesRecursive(QuadTree *tree, float screen_lat_min, float screen
|
||||||
lineRGBA(renderer, x1, y1, x2, y2, style.mapInnerColor.r, style.mapInnerColor.g, style.mapInnerColor.b, 255);
|
lineRGBA(renderer, x1, y1, x2, y2, style.mapInnerColor.r, style.mapInnerColor.g, style.mapInnerColor.b, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Debug quadtree
|
// //Debug quadtree
|
||||||
int tl_x,tl_y,tr_x,tr_y,bl_x,bl_y,br_x,br_y;
|
// int tl_x,tl_y,tr_x,tr_y,bl_x,bl_y,br_x,br_y;
|
||||||
float dx,dy;
|
// float dx,dy;
|
||||||
|
|
||||||
pxFromLonLat(&dx, &dy, tree->lat_min, tree->lon_min);
|
// pxFromLonLat(&dx, &dy, tree->lon_min, tree->lat_min);
|
||||||
screenCoords(&tl_x, &tl_y, dx, dy);
|
// screenCoords(&tl_x, &tl_y, dx, dy);
|
||||||
|
|
||||||
pxFromLonLat(&dx, &dy, tree->lat_max, tree->lon_min);
|
// pxFromLonLat(&dx, &dy, tree->lon_max, tree->lat_min);
|
||||||
screenCoords(&tr_x, &tr_y, dx, dy);
|
// screenCoords(&tr_x, &tr_y, dx, dy);
|
||||||
|
|
||||||
pxFromLonLat(&dx, &dy, tree->lat_min, tree->lon_max);
|
// pxFromLonLat(&dx, &dy, tree->lon_min, tree->lat_max);
|
||||||
screenCoords(&bl_x, &bl_y, dx, dy);
|
// screenCoords(&bl_x, &bl_y, dx, dy);
|
||||||
|
|
||||||
pxFromLonLat(&dx, &dy, tree->lat_max, tree->lon_max);
|
// pxFromLonLat(&dx, &dy, tree->lon_max, tree->lat_max);
|
||||||
screenCoords(&br_x, &br_y, dx, dy);
|
// screenCoords(&br_x, &br_y, dx, dy);
|
||||||
|
|
||||||
lineRGBA(renderer, tl_x, tl_y, tr_x, tr_y, 255, 0, 0, 255);
|
// lineRGBA(renderer, tl_x, tl_y, tr_x, tr_y, 50, 50, 50, 255);
|
||||||
lineRGBA(renderer, tr_x, tr_y, br_x, br_y, 255, 0, 0, 255);
|
// lineRGBA(renderer, tr_x, tr_y, br_x, br_y, 50, 50, 50, 255);
|
||||||
lineRGBA(renderer, bl_x, bl_y, br_x, br_y, 255, 0, 0, 255);
|
// lineRGBA(renderer, bl_x, bl_y, br_x, br_y, 50, 50, 50, 255);
|
||||||
lineRGBA(renderer, tl_x, tl_y, bl_x, bl_y, 255, 0, 0, 255);
|
// lineRGBA(renderer, tl_x, tl_y, bl_x, bl_y, 50, 50, 50, 255);
|
||||||
|
|
||||||
|
// // pixelRGBA(renderer,tl_x, tl_y,255,0,0,255);
|
||||||
|
// pixelRGBA(renderer,tr_x, tr_y,0,255,0,255);
|
||||||
|
// pixelRGBA(renderer,bl_x, bl_y,0,0,255,255);
|
||||||
|
// pixelRGBA(renderer,br_x, br_y,255,255,0,255);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
137
mapconverter.py
137
mapconverter.py
|
@ -1,103 +1,62 @@
|
||||||
import geopandas
|
import geopandas
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
import zipfile
|
||||||
|
from io import BytesIO
|
||||||
|
#from urllib.request import urlopen
|
||||||
|
import requests
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
|
||||||
outlist = []
|
def convertLinestring(linestring):
|
||||||
|
outlist = []
|
||||||
tolerance = .05
|
|
||||||
|
|
||||||
coast = geopandas.read_file("ne_10m_coastline.shp")
|
|
||||||
|
|
||||||
coast_simple = coast['geometry'].simplify(tolerance, preserve_topology=False)
|
|
||||||
|
|
||||||
for i in tqdm(range(len(coast_simple))):
|
|
||||||
pointx = coast_simple[i].coords.xy[0]
|
|
||||||
pointy = coast_simple[i].coords.xy[1]
|
|
||||||
|
|
||||||
|
pointx = linestring.coords.xy[0]
|
||||||
|
pointy = linestring.coords.xy[1]
|
||||||
|
|
||||||
for j in range(len(pointx)):
|
for j in range(len(pointx)):
|
||||||
outlist.extend([float(pointx[j]),float(pointy[j])])
|
outlist.extend([float(pointx[j]),float(pointy[j])])
|
||||||
|
|
||||||
outlist.extend([0,0])
|
outlist.extend([0,0])
|
||||||
|
return outlist
|
||||||
|
|
||||||
|
def extractLines(shapefile, tolerance):
|
||||||
|
print("Extracting map lines")
|
||||||
|
outlist = []
|
||||||
|
simplified = shapefile['geometry'].simplify(tolerance, preserve_topology=False)
|
||||||
|
|
||||||
|
for i in tqdm(range(len(simplified))):
|
||||||
|
if(simplified[i].geom_type == "LineString"):
|
||||||
|
outlist.extend(convertLinestring(simplified[i]))
|
||||||
|
|
||||||
|
elif(simplified[i].geom_type == "MultiPolygon" or simplified[i].geom_type == "Polygon"):
|
||||||
|
|
||||||
|
if(simplified[i].boundary.geom_type == "MultiLineString"):
|
||||||
|
for boundary in simplified[i].boundary:
|
||||||
|
outlist.extend(convertLinestring(boundary))
|
||||||
|
else:
|
||||||
|
outlist.extend(convertLinestring(simplified[i].boundary))
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("Unsupported type: " + simplified[i].geom_type)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return outlist
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='viz1090 Natural Earth Data Map Converter')
|
||||||
|
parser.add_argument("--tolerance", default=0.001, type=float, help="map simplification tolerance")
|
||||||
|
parser.add_argument("--scale", default="10m", choices=["10m","50m","110m"], type=str, help="map file scale")
|
||||||
|
parser.add_argument("mapfile", type=str, help="shapefile to load (e.g., from https://www.naturalearthdata.com/downloads/")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
shapefile = geopandas.read_file(args.mapfile)
|
||||||
|
|
||||||
|
outlist = extractLines(shapefile, args.tolerance)
|
||||||
|
|
||||||
bin_file = open("mapdata.bin", "wb")
|
bin_file = open("mapdata.bin", "wb")
|
||||||
np.asarray(outlist).astype(np.single).tofile(bin_file)
|
np.asarray(outlist).astype(np.single).tofile(bin_file)
|
||||||
bin_file.close()
|
bin_file.close()
|
||||||
|
|
||||||
print("Wrote %d points" % (len(outlist) / 2))
|
print("Wrote %d points" % (len(outlist) / 2))
|
||||||
|
|
||||||
# import json
|
|
||||||
# import numpy as np
|
|
||||||
# import sys
|
|
||||||
# from tqdm import tqdm
|
|
||||||
# import argparse
|
|
||||||
|
|
||||||
# parser = argparse.ArgumentParser(description='viz1090 SVG Map Converter')
|
|
||||||
# parser.add_argument("--resolution", default=250, type=int, help="downsample resolution")
|
|
||||||
# parser.add_argument("file", nargs="+", help="filename")
|
|
||||||
|
|
||||||
# args = parser.parse_args()
|
|
||||||
|
|
||||||
# if(len(args.file) == 0):
|
|
||||||
# print("No input filename given")
|
|
||||||
# exit()
|
|
||||||
|
|
||||||
# bin_file = open("mapdata.bin", "wb")
|
|
||||||
|
|
||||||
# outlist = []
|
|
||||||
|
|
||||||
# resolution = args.resolution
|
|
||||||
|
|
||||||
# for file in args.file:
|
|
||||||
# with open(file, "r") as read_file:
|
|
||||||
# data = json.load(read_file)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# print("Reading points")
|
|
||||||
# for i in tqdm(range(len(data['features']))):
|
|
||||||
|
|
||||||
|
|
||||||
# if(data['features'][i]['geometry']['type'] == 'LineString'):
|
|
||||||
# prevx = 0
|
|
||||||
# prevy = 0
|
|
||||||
|
|
||||||
# temp = []
|
|
||||||
|
|
||||||
# for currentPoint in data['features'][i]['geometry']['coordinates']:
|
|
||||||
|
|
||||||
# currentx = float(int(resolution * float(currentPoint[0]))) / resolution
|
|
||||||
# currenty = float(int(resolution * float(currentPoint[1]))) / resolution
|
|
||||||
|
|
||||||
# if(currentx != prevx or currenty != prevy):
|
|
||||||
# temp.extend([currentx,currenty])
|
|
||||||
|
|
||||||
# prevx = currentx
|
|
||||||
# prevy = currenty
|
|
||||||
# temp.extend(["0","0"])
|
|
||||||
# else:
|
|
||||||
# prevx = 0
|
|
||||||
# prevy = 0
|
|
||||||
|
|
||||||
# temp = []
|
|
||||||
|
|
||||||
# for currentLine in data['features'][i]['geometry']['coordinates']:
|
|
||||||
# for currentPoint in currentLine:
|
|
||||||
|
|
||||||
# currentx = float(int(resolution * float(currentPoint[0]))) / resolution
|
|
||||||
# currenty = float(int(resolution * float(currentPoint[1]))) / resolution
|
|
||||||
|
|
||||||
# if(currentx != prevx or currenty != prevy):
|
|
||||||
# temp.extend([currentx,currenty])
|
|
||||||
|
|
||||||
# prevx = currentx
|
|
||||||
# prevy = currenty
|
|
||||||
|
|
||||||
# temp.extend(["0","0"])
|
|
||||||
|
|
||||||
|
|
||||||
# outlist.extend(temp)
|
|
||||||
|
|
||||||
# np.asarray(outlist).astype(np.single).tofile(bin_file)
|
|
||||||
# bin_file.close()
|
|
||||||
|
|
||||||
# print("Wrote %d points" % (len(outlist) / 2))
|
|
Loading…
Reference in a new issue