Routing added on maps
This commit is contained in:
parent
e737bcab38
commit
9a2e507c24
3 changed files with 55 additions and 11 deletions
File diff suppressed because one or more lines are too long
|
|
@ -15,7 +15,7 @@ from urllib.request import Request, urlopen
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
|
|
||||||
VERSION = "2026-05-31-infographic-v5-single-location-footer-fix"
|
VERSION = "2026-07-01-road-route-no-distance"
|
||||||
|
|
||||||
MAP_WIDTH = 906
|
MAP_WIDTH = 906
|
||||||
MAP_HEIGHT = 510
|
MAP_HEIGHT = 510
|
||||||
|
|
@ -26,6 +26,7 @@ CLUSTER_RADIUS_KM = 2.0
|
||||||
FOOTER_HEIGHT = 118
|
FOOTER_HEIGHT = 118
|
||||||
FOOTER_MARGIN = 14
|
FOOTER_MARGIN = 14
|
||||||
USER_AGENT = "gpsmap-infographic-script/1.0"
|
USER_AGENT = "gpsmap-infographic-script/1.0"
|
||||||
|
OSRM_ROUTE_URL = "https://router.project-osrm.org/route/v1/driving"
|
||||||
|
|
||||||
PHOTO_EXTENSIONS = {
|
PHOTO_EXTENSIONS = {
|
||||||
".jpg", ".jpeg", ".png", ".heic", ".heif", ".webp", ".tif", ".tiff"
|
".jpg", ".jpeg", ".png", ".heic", ".heif", ".webp", ".tif", ".tiff"
|
||||||
|
|
@ -168,6 +169,47 @@ def haversine_km(lat1, lon1, lat2, lon2):
|
||||||
return 2 * r * math.atan2(math.sqrt(a), math.sqrt(1 - a))
|
return 2 * r * math.atan2(math.sqrt(a), math.sqrt(1 - a))
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_road_route(points):
|
||||||
|
"""Return route geometry as [(lat, lon), ...], falling back to straight points.
|
||||||
|
|
||||||
|
Uses OSRM's public OpenStreetMap-based demo router. The footer intentionally
|
||||||
|
does not show route distance; this is only for drawing a more realistic path.
|
||||||
|
"""
|
||||||
|
if len(points) < 2:
|
||||||
|
return points
|
||||||
|
|
||||||
|
try:
|
||||||
|
coords = ";".join(f"{lon:.6f},{lat:.6f}" for lat, lon in points)
|
||||||
|
params = urlencode({
|
||||||
|
"overview": "full",
|
||||||
|
"geometries": "geojson",
|
||||||
|
"steps": "false",
|
||||||
|
})
|
||||||
|
url = f"{OSRM_ROUTE_URL}/{coords}?{params}"
|
||||||
|
request = Request(url, headers={"User-Agent": USER_AGENT})
|
||||||
|
|
||||||
|
with urlopen(request, timeout=30) as response:
|
||||||
|
data = json.loads(response.read().decode("utf-8"))
|
||||||
|
|
||||||
|
if data.get("code") != "Ok":
|
||||||
|
message = data.get("message") or data.get("code") or "unknown OSRM error"
|
||||||
|
raise RuntimeError(message)
|
||||||
|
|
||||||
|
route = (data.get("routes") or [{}])[0]
|
||||||
|
coordinates = route.get("geometry", {}).get("coordinates") or []
|
||||||
|
road_points = [(float(lat), float(lon)) for lon, lat in coordinates]
|
||||||
|
|
||||||
|
if len(road_points) < 2:
|
||||||
|
raise RuntimeError("empty route geometry")
|
||||||
|
|
||||||
|
print(f"Road route: {len(road_points)} geometry points")
|
||||||
|
return road_points
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Road route unavailable; using straight lines: {e}")
|
||||||
|
return points
|
||||||
|
|
||||||
|
|
||||||
def collect_media(media_dir):
|
def collect_media(media_dir):
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
|
|
@ -604,19 +646,18 @@ def main():
|
||||||
|
|
||||||
clusters = make_clusters(items)
|
clusters = make_clusters(items)
|
||||||
points = [(c.lat, c.lon) for c in clusters]
|
points = [(c.lat, c.lon) for c in clusters]
|
||||||
|
route_latlon = fetch_road_route(points)
|
||||||
|
view_points = (route_latlon + points) if len(route_latlon) > 1 else points
|
||||||
|
|
||||||
zoom = choose_zoom(points, MAP_WIDTH, MAP_HEIGHT)
|
zoom = choose_zoom(view_points, MAP_WIDTH, MAP_HEIGHT)
|
||||||
top_left_x, top_left_y = map_view(points, zoom)
|
top_left_x, top_left_y = map_view(view_points, zoom)
|
||||||
|
|
||||||
label = make_map_label(items, clusters)
|
label = make_map_label(items, clusters)
|
||||||
photo_count = sum(1 for i in items if i.kind == "photo")
|
photo_count = sum(1 for i in items if i.kind == "photo")
|
||||||
video_count = sum(1 for i in items if i.kind == "video")
|
video_count = sum(1 for i in items if i.kind == "video")
|
||||||
distance = round(route_distance_km(clusters))
|
|
||||||
stats_parts = [plural(photo_count, "photo")]
|
stats_parts = [plural(photo_count, "photo")]
|
||||||
if video_count:
|
if video_count:
|
||||||
stats_parts.append(plural(video_count, "video"))
|
stats_parts.append(plural(video_count, "video"))
|
||||||
if len(clusters) > 1 and distance > 0:
|
|
||||||
stats_parts.append(f"{distance} km route")
|
|
||||||
stats = " • ".join(stats_parts)
|
stats = " • ".join(stats_parts)
|
||||||
date_text = format_date_range(items)
|
date_text = format_date_range(items)
|
||||||
print(f"Map label: {label}")
|
print(f"Map label: {label}")
|
||||||
|
|
@ -627,10 +668,11 @@ def main():
|
||||||
image = render_tiles(top_left_x, top_left_y, zoom)
|
image = render_tiles(top_left_x, top_left_y, zoom)
|
||||||
draw = ImageDraw.Draw(image)
|
draw = ImageDraw.Draw(image)
|
||||||
|
|
||||||
route_points = [screen_xy(c.lat, c.lon, zoom, top_left_x, top_left_y) for c in clusters]
|
route_points = [screen_xy(lat, lon, zoom, top_left_x, top_left_y) for lat, lon in route_latlon]
|
||||||
draw_route(draw, route_points)
|
draw_route(draw, route_points)
|
||||||
|
|
||||||
for cluster, (x, y) in zip(clusters, route_points):
|
marker_points = [screen_xy(c.lat, c.lon, zoom, top_left_x, top_left_y) for c in clusters]
|
||||||
|
for cluster, (x, y) in zip(clusters, marker_points):
|
||||||
draw_cluster_marker(draw, x, y, cluster)
|
draw_cluster_marker(draw, x, y, cluster)
|
||||||
|
|
||||||
draw_bottom_title(draw, label, stats, date_text)
|
draw_bottom_title(draw, label, stats, date_text)
|
||||||
|
|
|
||||||
|
|
@ -662,10 +662,12 @@ $siteHeaderTitle = $headerTitles[array_rand($headerTitles)];
|
||||||
<video controls controlsList="nodownload" oncontextmenu="return false" preload="metadata" src="<?=h($url)?>"<?= $posterUrl !== '' ? ' poster="'.h($posterUrl).'"' : '' ?>></video>
|
<video controls controlsList="nodownload" oncontextmenu="return false" preload="metadata" src="<?=h($url)?>"<?= $posterUrl !== '' ? ' poster="'.h($posterUrl).'"' : '' ?>></video>
|
||||||
<?php
|
<?php
|
||||||
$map = pathinfo($name, PATHINFO_FILENAME)."_map.png";
|
$map = pathinfo($name, PATHINFO_FILENAME)."_map.png";
|
||||||
|
$mapPath = $config['videos_dir']."/".$map;
|
||||||
|
$mapUrl = "/" . ltrim((string)$config['public_videos'], "/") . "/" . rawurlencode($map) . (is_file($mapPath) ? "?v=" . filemtime($mapPath) : "");
|
||||||
$hideMap = !empty($hiddenMapOutputs[$name]);
|
$hideMap = !empty($hiddenMapOutputs[$name]);
|
||||||
if(!$hideMap && is_file($config['videos_dir']."/".$map)):
|
if(!$hideMap && is_file($mapPath)):
|
||||||
?>
|
?>
|
||||||
<a class="map-image-link" href="<?=h("/" . ltrim((string)$config['public_videos'], "/") . "/" . rawurlencode($map))?>"><img class="map-image" src="<?=h("/" . ltrim((string)$config['public_videos'], "/") . "/" . rawurlencode($map))?>" alt="Map"></a>
|
<a class="map-image-link" href="<?=h($mapUrl)?>"><img class="map-image" src="<?=h($mapUrl)?>" alt="Map"></a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="video-info">
|
<div class="video-info">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue