#编码 , #分享
最近终于把大发哥的地图功能算是移植过来了,可以在 https://1900.live/map/ 页面中查看初步效果,不过在后续如何方便的维护这些数据的方式上产生了一些思考。
想直接看代码可以通过右侧TOC跳转到最后。
数据持久化无非是写在文件里或者数据库,所以我想了一下几种方案:
不过一种方案有些麻烦,大概只在我脑子里停留了2.5秒后就被我踢出局了。
第二个方案我倒是认真想了很久,甚至做了一下原型,验证结果是做虽然可以做,但是很麻烦。
因为相当于我得重新写一套包含前后端的,基于mapbox的交互,如添加标点,删除标点、更新标点,如果要全部弄完很是挺麻烦的,我也没这个信心做好,所以在第二天放弃了。
emmm,好像没有现成的API服务。
我查了一下相关的自制地图服务,好像基本上都没有提供这个功能。
倒是我之前用的google地图的 mymap 服务倒是提供了一个导出功能,不过也没有提供现成的API读取数据。
本来是打算放弃了,采用第二种方案。
但是无意间发现 mymap 的导出链接似乎是固定的,我通过开启无痕浏览,在其他设备上使用链接下载,等方式验证,都是通过的,应该是不需要cookie或者密钥之类的东西,有链接就可以下载。
不过下载下来的格式是.kmz的,无法直接使用,我尝试通过google到的一些在线工具转换后发现可以转换成一种geojson格式,所以我灵光一闪「能不能用workflow去下载mymap的数据再转换成geojson数据存在仓库里以供调用呢?」
所以我 用ChatGPT40写了个脚本测试,果然行得通。
只不过似乎定位坐标的转换不是很准确,会有一些偏差,可能是mapbox和google map之间的标准不一样。
不过这些之后可以慢慢处理。
目前虽然数据能正常渲染了,但是地点相关的游记这个功能暂时还没做,不过我大概有一些想法了。
首先数据结构可能要重新做下调整,大发哥那边不知道是通过什么工具维护数据的,他的 description
属性是以个数组对象,而且还包含有 permalink
属性和 image
属性,google map目前还不能这样做,所以我目前只能先把地点图片、游记标题和链接直接拼成字符串,在python脚本中再进分割处理。
其次,我想通过博客集成的 fuse.js
去搜索全站文章中和当前地点名字相关的博文进行展示,这部分可以在地图初始化的时候处理。
大概就是这样,之后再慢慢进行完善把。
首先你需要一个库,我这边用的我转换douban数据的库。
新增一个新的wordflow配置文件 .github/workflows/mapsync.yml
,这个action可以手动触发或者每天凌晨2点执行一次。
先通过链接下载kmz文件,再通过仓库的pyhon脚本转换数据为json格式存在 data
目录下。
然后在仓库根目录新增一个python脚本 convert_kmz_to_geojson.py
,内容如下:
import os
import sys
import json
import zipfile
import xml.etree.ElementTree as ET
from shapely.geometry import shape, Point, LineString, Polygon, mapping
def convert_kmz_to_geojson(kmz_file_path, geojson_file_path):
with zipfile.ZipFile(kmz_file_path, 'r') as kmz:
kml_content = kmz.read('doc.kml')
root = ET.fromstring(kml_content)
geojson_content = kml_to_geojson(root)
with open(geojson_file_path, 'w') as geojson_file:
json.dump(geojson_content, geojson_file, indent=4)
def kml_to_geojson(element):
geojson = {
"type": "FeatureCollection",
"features": []
}
for placemark in element.iterfind('.//{http://www.opengis.net/kml/2.2}Placemark'):
feature = {
"type": "Feature",
"properties": {},
"geometry": None
}
for name in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}name'):
feature["properties"]["name"] = name.text
for description in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}description'):
feature["properties"]["description"] = description.text
for point in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}Point'):
coords = point.find('{http://www.opengis.net/kml/2.2}coordinates').text.strip()
lon, lat, _ = map(float, coords.split(','))
feature["geometry"] = mapping(Point(lon, lat))
for linestring in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}LineString'):
coords = linestring.find('{http://www.opengis.net/kml/2.2}coordinates').text.strip()
points = [tuple(map(float, coord.split(',')))[:2] for coord in coords.split()]
feature["geometry"] = mapping(LineString(points))
for polygon in placemark.iterfind('.//{http://www.opengis.net/kml/2.2}Polygon'):
outer_boundary = polygon.find('.//{http://www.opengis.net/kml/2.2}outerBoundaryIs')
if outer_boundary is not None:
coords = outer_boundary.find('.//{http://www.opengis.net/kml/2.2}coordinates').text.strip()
points = [tuple(map(float, coord.split(',')))[:2] for coord in coords.split()]
feature["geometry"] = mapping(Polygon([points]))
if feature["geometry"] is not None:
geojson["features"].append(feature)
return geojson
if __name__ == "__main__":
kmz_file_path = sys.argv[1]
geojson_file_path = sys.argv[2]
convert_kmz_to_geojson(kmz_file_path, geojson_file_path)
然后前端使用这个数据文件即可。
加入评论