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) {
|
||||
|
||||
if(depth > 25) {
|
||||
// printf("fail [%f %f] -> [%f %f]\n",line->start.lon,line->start.lat,line->end.lon,line->end.lat);
|
||||
// if(depth > 25) {
|
||||
// // 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);
|
||||
// fflush(stdout);
|
||||
tree->lines.push_back(&(*line));
|
||||
return true;
|
||||
}
|
||||
// // printf("bounds %f %f %f %f\n",tree->lon_min, tree->lon_max, tree->lat_min, tree->lat_max);
|
||||
// // fflush(stdout);
|
||||
// tree->lines.push_back(&(*line));
|
||||
// return true;
|
||||
// }
|
||||
|
||||
|
||||
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_max;
|
||||
|
||||
if (!startInside || !endInside) {
|
||||
return false;
|
||||
}
|
||||
// if (!startInside && !endInside) {
|
||||
// if (!startInside || !endInside) {
|
||||
// return false;
|
||||
// }
|
||||
if (!startInside && !endInside) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if (startInside != endInside) {
|
||||
// tree->lines.push_back(&(*line));
|
||||
// return true;
|
||||
// }
|
||||
if (startInside != endInside) {
|
||||
tree->lines.push_back(&(*line));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tree->nw == NULL) {
|
||||
tree->nw = new QuadTree;
|
||||
|
|
41
README.md
41
README.md
|
@ -1,12 +1,20 @@
|
|||
# viz1090
|
||||
|
||||
**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!)
|
||||
* Integration with handheld features like GPS, battery monitors, buttons/dials, etc.
|
||||
* Android build is currently broken
|
||||
|
||||
### BUILDING
|
||||
|
||||
|
@ -34,30 +42,21 @@ make clean; make
|
|||
```
|
||||
|
||||
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
|
||||
pip3 install lxml numpy tqdm
|
||||
./getmap.sh
|
||||
pip3 install geopandas tqdm
|
||||
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:
|
||||
|
||||
```
|
||||
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
|
||||
```
|
||||
The default parameters for mapconverter should render resonably quickly on a Raspberri Pi 4. See the mapconverter section below for other options.
|
||||
|
||||
|
||||
3. (Windows only)
|
||||
|
|
39
View.cpp
39
View.cpp
|
@ -292,7 +292,7 @@ void View::font_init() {
|
|||
// 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 lightblue = {211,208,203,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);
|
||||
}
|
||||
|
||||
//Debug quadtree
|
||||
int tl_x,tl_y,tr_x,tr_y,bl_x,bl_y,br_x,br_y;
|
||||
float dx,dy;
|
||||
// //Debug quadtree
|
||||
// int tl_x,tl_y,tr_x,tr_y,bl_x,bl_y,br_x,br_y;
|
||||
// float dx,dy;
|
||||
|
||||
pxFromLonLat(&dx, &dy, tree->lat_min, tree->lon_min);
|
||||
screenCoords(&tl_x, &tl_y, dx, dy);
|
||||
// pxFromLonLat(&dx, &dy, tree->lon_min, tree->lat_min);
|
||||
// screenCoords(&tl_x, &tl_y, dx, dy);
|
||||
|
||||
pxFromLonLat(&dx, &dy, tree->lat_max, tree->lon_min);
|
||||
screenCoords(&tr_x, &tr_y, dx, dy);
|
||||
// pxFromLonLat(&dx, &dy, tree->lon_max, tree->lat_min);
|
||||
// screenCoords(&tr_x, &tr_y, dx, dy);
|
||||
|
||||
pxFromLonLat(&dx, &dy, tree->lat_min, tree->lon_max);
|
||||
screenCoords(&bl_x, &bl_y, dx, dy);
|
||||
// pxFromLonLat(&dx, &dy, tree->lon_min, tree->lat_max);
|
||||
// screenCoords(&bl_x, &bl_y, dx, dy);
|
||||
|
||||
// pxFromLonLat(&dx, &dy, tree->lon_max, tree->lat_max);
|
||||
// screenCoords(&br_x, &br_y, dx, dy);
|
||||
|
||||
// lineRGBA(renderer, tl_x, tl_y, tr_x, tr_y, 50, 50, 50, 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, 50, 50, 50, 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);
|
||||
|
||||
pxFromLonLat(&dx, &dy, tree->lat_max, tree->lon_max);
|
||||
screenCoords(&br_x, &br_y, dx, dy);
|
||||
|
||||
lineRGBA(renderer, tl_x, tl_y, tr_x, tr_y, 255, 0, 0, 255);
|
||||
lineRGBA(renderer, tr_x, tr_y, br_x, br_y, 255, 0, 0, 255);
|
||||
lineRGBA(renderer, bl_x, bl_y, br_x, br_y, 255, 0, 0, 255);
|
||||
lineRGBA(renderer, tl_x, tl_y, bl_x, bl_y, 255, 0, 0, 255);
|
||||
}
|
||||
|
||||
|
||||
|
|
131
mapconverter.py
131
mapconverter.py
|
@ -1,103 +1,62 @@
|
|||
import geopandas
|
||||
import numpy as np
|
||||
from tqdm import tqdm
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
#from urllib.request import urlopen
|
||||
import requests
|
||||
import argparse
|
||||
import os
|
||||
|
||||
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)):
|
||||
outlist.extend([float(pointx[j]),float(pointy[j])])
|
||||
|
||||
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")
|
||||
np.asarray(outlist).astype(np.single).tofile(bin_file)
|
||||
bin_file.close()
|
||||
|
||||
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