これは日々の作業を通して学んだことや毎日の生活で気づいたことをを記録しておく備忘録である。
HTML ファイル生成日時: 2025/12/23 00:01:16.512 (台灣標準時)
Python を使って、地図をダウンロードし、その地図上に GPS で記録した移動 の軌跡を描く方法を調べてみたでござる。以下はその記録でござる。
希望する領域の地図をダウンロードする方法は、「Python を使って指定した範 囲の地図をダウンロードする方法」に記録したでござる。
GPX ファイルを読み込み、 GPS データを取得する方法は、「GPX ファイルを読み込んで 経度緯度標高を抽出する方法」に記録したでござる。
ダウンロードした地図上に点を打ったり、線を描いたりするには、座標として 経度と緯度を使ってプロットすればよいようでござる。
以下のようなスクリプトを用意してみたでござる。
#!/usr/pkg/bin/python3 # # Time-stamp: <2025/12/22 23:54:39 (UT+08:00) daisuke> # # importing argparse module import argparse # importing sys module import sys # importing pathlib module import pathlib # importing numpy module import numpy # importing gpxpy module import gpxpy import gpxpy.gpx # importing contextily module import contextily # importing matplotlib module import matplotlib.backends.backend_agg import matplotlib.figure # initialising a parser parser = argparse.ArgumentParser (description='Showing GPX tracks on a map') # choices list_mapsource = ('mapnik', 'opentopomap') list_zoomadjust = (0, 1) # adding arguments parser.add_argument ('-o', '--output', default='map.png', \ help='output file (default: map.png)') parser.add_argument ('-l', '--longitude', type=float, default=-999.9, \ help='longitude in degree') parser.add_argument ('-b', '--latitude', type=float, default=-999.9, \ help='latitude in degree') parser.add_argument ('-w', '--width', type=float, default=-999.9, \ help='width of the map in degree') parser.add_argument ('-s', '--mapsource', default='mapnik', \ choices=list_mapsource, \ help='source of the map (default: mapnik)') parser.add_argument ('-m', '--margin', type=float, default=40.0, \ help='margin of the map in percent (default: 40.0)') parser.add_argument ('-r', '--resolution', type=float, default=225.0, \ help='resolution of output file in DPI (default: 225)') parser.add_argument ('-z', '--zoomadjust', type=int, default=0, \ choices=list_zoomadjust, \ help='zoom level adjustment (default: 0)') parser.add_argument ('files', nargs='+', help='input GPX files') # parsing arguments args = parser.parse_args () # input parameters file_output = args.output longitude_centre = args.longitude latitude_centre = args.latitude width_deg = args.width map_source = args.mapsource resolution_dpi = args.resolution zoom_adjust = args.zoomadjust list_input_files = args.files margin_percent = args.margin # map source if (map_source == 'mapnik'): source = contextily.providers.OpenStreetMap.Mapnik elif (map_source == 'opentopomap'): source = contextily.providers.OpenTopoMap # check of longitude, latitude, and width if ( ( (longitude_centre != -999.9) and (latitude_centre != -999.9) \ and (width_deg != -999.9) ) and ( (longitude_centre < -180.0) or (longitude_centre > +180.0) \ or (latitude_centre < -90.0) or (latitude_centre > +90.0) \ or (width_deg <= 0.0) ) ): # printing message print (f'#') print (f'# ERROR: Something is wrong with longitude or latitude or width!') print (f'# ERROR: Longitude: {longitude_centre} deg') print (f'# ERROR: Latitude: {latitude_centre} deg') print (f'# ERROR: Width: {width_deg} deg') print (f'#') # stopping the script sys.exit (0) # making an empty list for storing data list_gpx_files = [] data_longitude = [] data_latitude = [] data_elevation = [] # initialisation of lon_min, lon_max, lat_min, and lat_max lon_min = +999999.9 lon_max = -999999.9 lat_min = +999999.9 lat_max = -999999.9 # making pathlib object path_output = pathlib.Path (file_output) # existing check of output file if (path_output.exists ()): # printing message print (f'#') print (f'# ERROR: output file \"{file_output}\" DOES exist!') print (f'# ERROR: stopping the script...') print (f'#') # stopping the script sys.exit (0) # processing GPX files one-by-one for file_gpx in list_input_files: # making pathlib object path_gpx = pathlib.Path (file_gpx) # if the file is NOT a GPX file, then skip if (path_gpx.suffix != '.gpx'): # printing message print (f'#') print (f'# WARNING: the file \"{file_gpx}\" is NOT a GPX file!') print (f'# WARNING: skipping the file \"{file_gpx}\"...') print (f'#') # skipping the file continue # if the file does not exist, the skip if not (path_gpx.exists ()): # printing message print (f'#') print (f'# WARNING: the file \"{file_gpx}\" does NOT exist!') print (f'# WARNING: skipping the file \"{file_gpx}\"...') print (f'#') # skipping the file continue # opening GPX file with open (file_gpx, 'r') as fh_in: # reading data from GPX file data_gpx = gpxpy.parse (fh_in) # making empty lists for storing data list_longitude = [] list_latitude = [] list_elevation = [] # extracting longitude, latitude, and elevation for track in data_gpx.tracks: for segment in track.segments: for point in segment.points: # appending data to lists list_longitude.append (point.longitude) list_latitude.append (point.latitude) list_elevation.append (point.elevation) # checking minimum and maximum values if (point.longitude < lon_min): lon_min = point.longitude if (point.longitude > lon_max): lon_max = point.longitude if (point.latitude < lat_min): lat_min = point.latitude if (point.latitude > lat_max): lat_max = point.latitude # appending data to data_longitude, data_latitude, and data_elevation data_longitude.append (list_longitude) data_latitude.append (list_latitude) data_elevation.append (list_elevation) # longitude and latitude centre of the map and width of the map if ( (longitude_centre < -999.0) and (latitude_centre < -999.0) \ and (width_deg < -999.0) ): longitude_centre = (lon_max + lon_min) / 2.0 latitude_centre = (lat_max + lat_min) / 2.0 width_longitude = lon_max - lon_min width_latitude = lat_max - lat_min if (width_longitude > width_latitude): width_deg = width_longitude * (1.0 + margin_percent / 100.0) else: width_deg = width_latitude * (1.0 + margin_percent / 100.0) # west end, east end, south end, and north end of the region to plot in deg west_end_deg = longitude_centre - width_deg * 0.5 east_end_deg = longitude_centre + width_deg * 0.5 south_end_deg = latitude_centre - width_deg * 0.5 north_end_deg = latitude_centre + width_deg * 0.5 region = (west_end_deg, east_end_deg, south_end_deg, north_end_deg) # creating fig, canvas, and ax objects fig = matplotlib.figure.Figure () canvas = matplotlib.backends.backend_agg.FigureCanvasAgg (fig) ax = fig.add_subplot (111) # settings of output map ax.set_aspect("equal") ax.axis ("off") ax.axis (region) if (zoom_adjust == 1): contextily.add_basemap (ax, crs="EPSG:4326", source=source, \ zoom_adjust=zoom_adjust) else: contextily.add_basemap (ax, crs="EPSG:4326", source=source) fig.subplots_adjust (left=0.0, right=1.0, bottom=0.0, top=1.0) # visualising GPX tracks for i in range ( len (data_longitude) ): ax.plot (data_longitude[i], data_latitude[i], \ linestyle='-', linewidth=2, color='red', alpha=0.7) ax.plot (data_longitude[i][0], data_latitude[i][0], \ linestyle='None', marker='o', markersize=7, \ color='blue', alpha=0.5) ax.plot (data_longitude[i][-1], data_latitude[i][-1], \ linestyle='None', marker='s', markersize=7, \ color='green', alpha=0.5) # creating a map fig.savefig (file_output, dpi=resolution_dpi, \ bbox_inches="tight", transparent=True)
実行してみた結果は以下の通りでござる。
% python3 contextily_show_gps_track_00.py -h usage: contextily_show_gps_track_00.py [-h] [-o OUTPUT] [-l LONGITUDE] [-b LATITUDE] [-w WIDTH] [-s {mapnik,opentopomap}] [-m MARGIN] [-r RESOLUTION] [-z {0,1}] files [files ...] Showing GPX tracks on a map positional arguments: files input GPX files options: -h, --help show this help message and exit -o, --output OUTPUT output file (default: map.png) -l, --longitude LONGITUDE longitude in degree -b, --latitude LATITUDE latitude in degree -w, --width WIDTH width of the map in degree -s, --mapsource {mapnik,opentopomap} source of the map (default: mapnik) -m, --margin MARGIN margin of the map in percent (default: 40.0) -r, --resolution RESOLUTION resolution of output file in DPI (default: 225) -z, --zoomadjust {0,1} zoom level adjustment (default: 0) % python3 contextily_show_gps_track_00.py -s mapnik -m 50 -z 1 -o luofu_mapnik.png luofu.gpx
|
|---|
複数の GPX ファイルの中のデータを読み込み、移動の軌跡を描いてみたでござる。
% python3 contextily_show_gps_track_00.py -s mapnik -m 50 -z 1 -o shueshan_mapnik.png shueshan_*.gpx
|
|---|