1. 线路规划(路书)

1.1. 需求

  1. 可以生成 GPX 文件导入码表
  2. 可以根据起点和终点自动规划路线,且能添加途径点
  3. 规划路线时可以方便查看海拔和坡度

1.2. 现有 APP

  1. 码表类 APP:
    • Garmin Connect
      • 没用过。
    • iGPSPORT
      • 支持高德规划算法,地图更新及时、规划路线好
      • 可以方便查看海拔和坡度
      • 不支持 GPX 导出,只能配合 iGPSPORT 码表使用。
  2. 运动、骑行类 APP:
    • Strava
      • 收费
      • 线路规划比较蠢
    • Komoot
      • 部分功能收费
    • 行者(推荐)
      • 支持高德规划算法
      • 可以导出 GPX
      • 可以方便查看海拔和坡度
      • 途径点调整不太方便
  3. 地图类 APP
    • 百度地图
      • 有路书功能,不支持导出 GPX
    • 高德地图
      • 仅支持导航,不支持导出 GPX
  4. 网页类 APP
    • brouter(推荐)
      • BRouter web client
      • bikerouter.de (m11n.de)
      • 采用 OpenStreetMap GIS 数据,大陆路线更新不及时
      • 支持起点到终点线路规划算法
      • 方便添加途径点
      • 方便查看海拔和坡度
      • 可以导出 GPX
      • 大陆访问较慢
    • gpx.studio(推荐)
      • gpx.studio — the online GPX file editor
      • 采用 OpenStreetMap GIS 数据,大陆路线更新不及时
      • 支持起点到终点线路规划算法(和 brouter 一样)
      • 支持起点到终点线路规划算法
      • 方便添加途径点
      • 方便查看海拔和坡度
      • 可以导出 GPX
      • 大陆访问较慢
      • 支持私有化部署

1.3. gpx.studio 私有化部署

1.3.1. Install gpx.studio

cd website
npm run preview -- --host

1.3.2. Install brouter

import requests
from bs4 import BeautifulSoup
import os
from urllib.parse import urljoin
from concurrent.futures import ThreadPoolExecutor
import time
failed_files = []
def download_file(file_url, dir_name):
    attempt = 0
    while attempt < 3:
        try:
            response = requests.get(file_url, timeout=10)
            response.raise_for_status()
            file_name = os.path.basename(file_url)
            with open(os.path.join(dir_name, file_name), 'wb') as file:
                file.write(response.content)
            print(f"Download: {file_name}")
            return
        except Exception as e:
            attempt += 1
            print(f"Success: {file_url} Fail: {e},Retry: {attempt}/{3}")
            if attempt < 3:
                time.sleep(5)
    print(f"Download {file_url} fail,max retry has reached")
    failed_files.append(file_url)
base_url = "https://brouter.de/brouter/segments4/"
dir_name = "segments4"
response = requests.get(base_url)
if response.status_code == 200:
    soup = BeautifulSoup(response.text, 'lxml')
    rd5_files = soup.select('a[href*="rd5"]')
    file_urls = []
    for rd5_file in rd5_files:
        file_urls.append(urljoin(base_url, rd5_file.get_text()))
    if not os.path.exists(dir_name):
        os.makedirs(dir_name)
    with ThreadPoolExecutor(max_workers=10) as executor:
        executor.map(lambda file_url: download_file(file_url, dir_name), file_urls)
    print(f"Fail Num: {len(failed_files)}")
    if failed_files:
        print("\nFiled List:")
        for file_url in failed_files:
            print(file_url)
else:
    print(f"Error,status code:{response.status_code}")
  • Run: ./misc/scripts/standalone/server.sh

1.3.3. Modify gpx.studio Routing Service Address

  • File: ./website/src/lib/components/toolbar/tools/routing/Routing.ts
async function getRoute( ... ) {
    let url = `[YOUR BROUTER SERVICE URL]?lonlats=...`;
    ...
}