Skip to content

Commit cb746c8

Browse files
srmainwaringpeterbarker
authored andcommitted
mavproxy_map: add terrain contours
- Add terrain contours using matplotlib. - Hide circles on contour polygons. - Add variables for number of contour levels and tiles. - Remove blank lines. - Add menu to show and hide terrain contours. - Add options for terrain contours. Signed-off-by: Rhys Mainwaring <[email protected]>
1 parent 8ef000e commit cb746c8

File tree

1 file changed

+147
-6
lines changed

1 file changed

+147
-6
lines changed

Diff for: MAVProxy/modules/mavproxy_map/__init__.py

+147-6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def __init__(self, mpstate):
2727
self.wp_change_time = 0
2828
self.fence_change_time = 0
2929
self.rally_change_time = 0
30+
self.terrain_contour_ids = []
3031
self.have_simstate = False
3132
self.have_vehicle = {}
3233
self.move_wp = -1
@@ -65,8 +66,12 @@ def __init__(self, mpstate):
6566
('showdirection', bool, False),
6667
('setpos_accuracy', float, 50),
6768
('mission_color', str, "white"),
68-
('font_size', float, 0.5) ])
69-
69+
('font_size', float, 0.5),
70+
('contour_levels', int, 20),
71+
('contour_grid_spacing', float, 30.0),
72+
('contour_grid_extent', float, 20000.0),
73+
])
74+
7075
service='MicrosoftHyb'
7176
if 'MAP_SERVICE' in os.environ:
7277
service = os.environ['MAP_SERVICE']
@@ -141,6 +146,12 @@ def __init__(self, mpstate):
141146
mavutil.mavlink.MAV_CMD_NAV_VTOL_LAND: "VL",
142147
}
143148

149+
self.add_menu(MPMenuSubMenu('Terrain', items=[
150+
MPMenuItem('Show Contours', returnkey='showTerrainContours'),
151+
MPMenuItem('Hide Contours', returnkey='hideTerrainContours'),
152+
MPMenuItem('Remove Contours', returnkey='removeTerrainContours'),
153+
]))
154+
144155
def add_menu(self, menu):
145156
'''add to the default popup menu'''
146157
from MAVProxy.modules.mavproxy_map import mp_slipmap
@@ -192,7 +203,6 @@ def print_google_maps_link(self):
192203
pos = self.mpstate.click_location
193204
print("https://www.google.com/maps/search/?api=1&query=%f,%f" % (pos[0], pos[1]))
194205

195-
196206
def write_JSON(self, fname, template, vardict):
197207
'''write a JSON file in log directory'''
198208
fname = os.path.join(self.logdir, fname)
@@ -268,8 +278,6 @@ def cmd_map_marker(self, args, latlon=None):
268278

269279
print("Wrote marker %s" % fname)
270280

271-
272-
273281
def cmd_map(self, args):
274282
'''map commands'''
275283
from MAVProxy.modules.mavproxy_map import mp_slipmap
@@ -650,7 +658,6 @@ def display_fence(self):
650658
else:
651659
self.map.remove_object('Fence')
652660

653-
654661
def closest_waypoint(self, latlon):
655662
'''find closest waypoint to a position'''
656663
(lat, lon) = latlon
@@ -778,6 +785,12 @@ def handle_menu_event(self, obj):
778785
self.print_google_maps_link()
779786
elif menuitem.returnkey == 'setServiceTerrain':
780787
self.module('terrain').cmd_terrain(['set', 'source', menuitem.get_choice()])
788+
elif menuitem.returnkey == 'showTerrainContours':
789+
self.display_terrain_contours()
790+
elif menuitem.returnkey == 'hideTerrainContours':
791+
self.hide_terrain_contours()
792+
elif menuitem.returnkey == 'removeTerrainContours':
793+
self.remove_terrain_contours()
781794

782795
def map_callback(self, obj):
783796
'''called when an event happens on the slipmap'''
@@ -1323,6 +1336,134 @@ def check_redisplay_rallypoints(self):
13231336
points.append((nearest_land_wp.x, nearest_land_wp.y))
13241337
self.map.add_object(mp_slipmap.SlipPolygon('Rally Land %u' % (i+1), points, 'RallyPoints', (255,255,0), 2))
13251338

1339+
def display_terrain_contours(self):
1340+
"""
1341+
Show terrain contours
1342+
"""
1343+
from MAVProxy.modules.mavproxy_map import mp_slipmap
1344+
import numpy as np
1345+
1346+
# configure matplotlib for non-gui use
1347+
import matplotlib
1348+
matplotlib.use('Agg')
1349+
1350+
# disable interactive plotting mode
1351+
import matplotlib.pyplot as plt
1352+
plt.ioff()
1353+
1354+
terrain_module = self.module('terrain')
1355+
if terrain_module is None:
1356+
return
1357+
1358+
elevation_model = terrain_module.ElevationModel
1359+
1360+
# show contours if they have already been calculated
1361+
if len(self.terrain_contour_ids) > 0:
1362+
self.show_terrain_contours()
1363+
return
1364+
1365+
# centre terrain grid about clicked location
1366+
if self.mpstate.click_location is None:
1367+
return
1368+
1369+
(lat, lon) = self.mpstate.click_location
1370+
1371+
# retrieve grid options from map settings
1372+
grid_spacing = self.map_settings.contour_grid_spacing
1373+
grid_extent = self.map_settings.contour_grid_extent
1374+
levels = self.map_settings.contour_levels
1375+
1376+
# create mesh grid
1377+
x = np.arange(-0.5 * grid_extent, 0.5 * grid_extent, grid_spacing)
1378+
y = np.arange(-0.5 * grid_extent, 0.5 * grid_extent, grid_spacing)
1379+
x_grid, y_grid = np.meshgrid(x, y)
1380+
1381+
def terrain_surface(lat, lon, x, y):
1382+
"""
1383+
Calculate terrain altitudes for the NED offsets (x, y)
1384+
centred on (lat, lon).
1385+
"""
1386+
alt = []
1387+
for east in y:
1388+
alt_y = []
1389+
for north in x:
1390+
(lat2, lon2) = mp_util.gps_offset(lat, lon, east, north)
1391+
alt_y.append(elevation_model.GetElevation(lat2, lon2))
1392+
alt.append(alt_y)
1393+
return alt
1394+
1395+
def ned_to_latlon(contours, lat, lon):
1396+
"""
1397+
Convert contour polygons in NED coordinates offset from (lat, lon)
1398+
to polygons in orthometric coordinates.
1399+
"""
1400+
contours_latlon = []
1401+
for polygons in contours:
1402+
polygons_latlon = []
1403+
for polygon in polygons:
1404+
polygon_latlon = []
1405+
for point in polygon:
1406+
(north, east) = point
1407+
(lat2, lon2) = mp_util.gps_offset(lat, lon, east, north)
1408+
polygon_latlon.append([lat2, lon2])
1409+
polygons_latlon.append(polygon_latlon)
1410+
contours_latlon.append(polygons_latlon)
1411+
return contours_latlon
1412+
1413+
# generate surface and contours
1414+
z_grid = np.array(terrain_surface(lat, lon, x, y))
1415+
_, (ax1) = plt.subplots(1, 1, figsize=(10,10))
1416+
cs = ax1.contour(x_grid, y_grid, z_grid, levels=levels)
1417+
contours = ned_to_latlon(cs.allsegs, lat, lon)
1418+
1419+
# add terrain layer and contour polygons
1420+
self.map.add_object(mp_slipmap.SlipClearLayer('Terrain'))
1421+
1422+
self.terrain_contour_ids.clear()
1423+
num_contours = len(contours)
1424+
for i in range(num_contours):
1425+
polygons = contours[i]
1426+
for j in range(len(polygons)):
1427+
p = polygons[j]
1428+
if len(p) > 1:
1429+
id = f"terrain {i} {j}"
1430+
self.terrain_contour_ids.append(id)
1431+
contour_colour = (255, 255, 255)
1432+
self.map.add_object(mp_slipmap.SlipPolygon(
1433+
id, p,
1434+
layer='Terrain', linewidth=1,
1435+
colour=contour_colour,
1436+
showcircles=False
1437+
))
1438+
1439+
def show_terrain_contours(self):
1440+
"""
1441+
Show terrain contours.
1442+
"""
1443+
# unhide polygons
1444+
for id in self.terrain_contour_ids:
1445+
self.map.hide_object(id, hide=False)
1446+
1447+
def hide_terrain_contours(self):
1448+
"""
1449+
Hide terrain contours.
1450+
"""
1451+
# hide polygons
1452+
for id in self.terrain_contour_ids:
1453+
self.map.hide_object(id, hide=True)
1454+
1455+
def remove_terrain_contours(self):
1456+
"""
1457+
Remove terrain contours and the terrain clear layer.
1458+
"""
1459+
# remove polygons
1460+
for id in self.terrain_contour_ids:
1461+
self.map.remove_object(id)
1462+
# remove layer
1463+
self.map.remove_object('Terrain')
1464+
self.terrain_contour_ids.clear()
1465+
1466+
13261467
def init(mpstate):
13271468
'''initialise module'''
13281469
return MapModule(mpstate)

0 commit comments

Comments
 (0)