Here 矢量数据下载
#!/usr/bin/env python3
"""
HERE地图矢量瓦片API客户端
HERE API只接受omv格式,但返回的数据实际是MVT(Mapbox Vector Tile)Protobuf格式
自动将瓦片像素坐标转换为WGS84经纬度坐标
"""

import requests
import mapbox_vector_tile
import json
import os
import sys
import math
from typing import Dict,List,Any,Optional

if sys.stdout.encoding =="gbk":
    sys.stdout.reconfigure(encoding="utf-8")

def _tile_px_to_lonlat(z:int,x:int,y:int,extent:int=4096):
    """
    创建mapbox-vector-tile坐标转换器
    将瓦片像素坐标(0-extent)转换为WGS84经纬度坐标
    新版本API:transformer接收(x,y)两个参数
    """
    def transform(px,py):
        py = extent - py
        world_x = (x + px / extent) / (2 ** z)
        world_y = (y + py / extent) / (2 ** z)
        lon = world_x * 360.0 - 180.0
        n = math.pi - world_y * 2.0 * math.pi
        lat = math.degrees(math.atan(math.sinh(n)))
        return [lon, lat]
    return transform


class HEREVectorTileClient:
    """HERE矢量瓦片API客户端"""

    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://vector.hereapi.com/v2/vectortiles/base/mc"

    def request_tile(self, z: int, x: int, y:int, layers:List[str]) -> Optional[bytes]:
        """请求瓦片数据(omv格式,实际是MVT Protobuf)"""
        url = f"{self.base_url}/{z}/{x}/{y}/omv"

        params = {"xnlp":"CL_JSMv3.2.3.0","apikey":self.api_key,"content":",".join(layers)}

        headers = {
            "accept": "*/*",
            "accept-encoding": "gzip, deflate, br, zstd",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
            "origin": "htps://wego.here.com",
            "priority": "u=1, i",
            "referer": "htps://wego.here.com/",
            "sec-ch-ua": '"Microsoft Edge";v="143","Chromium";v="143","Not A(Brand";v="24"',
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-platform": '"Windows"',
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "cross-site",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0"
        }

        try:
            print(f"[请求] Z={z}, X={x}, Y={y}")
            print(f"   URL: {url}")
            print(f"   图层:{layers}")

            response = requests.get(url, params= params, headers= headers, timeout= 30)

            print(f"   状态码:{response.status_code}")
            print(f"   Content-Type: {response.headers.get('Content-Type')}")

            if response.status_code != 200:
                try:
                    error = response.json()
                    print(f"[错误] {json.dumps(error, indent= 2, ensure_ascii= False)}")
                except:
                    print(f"[错误] {response.text[:500]}")
                return None

            data = response.content
            print(f"[OK] 数据大小:{len(data)} bytes")
            return data

        except requests.exceptions.RequestException as e:
            print(f"[错误] 请求失败:{str(e)}")
            return None

    def parse_to_geojson(self, data: bytes, z: int, x: int, y:int) -> Optional[Dict]:
        """将MVT Protobuf数据解析为Geojson(自动转换坐标为经纬度)"""
        try:
            transformer = _tile_px_to_lonlat(z, x, y)
            parsed = mapbox_vector_tile.decode(data, default_options={'transformer': transformer})

            total_features = sum(len(layer.get("features", [])) for layer in parsed.value())
            print(f"[解析] 共 {len(parsed)} 个图层, {total_features} 个要素 (坐标已转WGS84)")

            for name, layer in parsed.items():
                print(f"      {name}: {len(layer.get('features', []))} 个要素")

            geojson = {
                "type": "FeatureCollection",
                "name": f"tile_{z}_{x}_{y}",
                "metadata":{
                    "tile_z": z,
                    "tile_x": x,
                    "tile_y": y,
                    "layers": list(parsed.keys()),
                    "crs": "EPSG:4326"
                },
                "features": []
            }

            for layer_name, layer_data in parsed.items():
                for feature in layer_data.get("features", []):
                    if "properties" not in feature:
                        feature["properties"] = {}
                    feature["properties"]["_layer"] = layer_name
                    geojson["features"].append(feature)

            return geojson

        except Exception as e:
            print(f"[错误] 解析失败:{str(e)}")
            return None

    def save_geojson(self, geojson: Dict, output_dir: str = "output") -> str:
        """保存Geojson到文件"""
        os.makedirs(output_dir, exist_ok= True)
        meta = geojson.get("metadata", {})
        z = meta.get("tile_z", 0)
        x = meta.get("tile_x", 0)
        y = meta.get("tile_y", 0)
        path = os.path.join(output_dir, f"tile_{z}_{x}_{y}.geojson")

        with open(path, "w", encoding= "utf-8") as f:
            json.dump(geojson, f, indent= 2, ensure_ascii= False)

        print(f"[保存] {path} ({len(geojson['features'])} 个要素,WGS经纬度)")
        return path

    def save_raw(self, data: bytes, z: int, x: int, y: int, output_dir: str = "output") -> str:
        """保存原始MVT数据"""
        os.makedirs(output_dir, exist_ok= True)
        path = os.path.join(output_dir, f"tile_{z}_{x}_{y}.mvt")
        with open(path, "wb") as f:
            f.write(data)
        print(f"[保存] {path} ({len(data)} bytes)")
        return path


def main():
    API_KEY = "Xy4hgfoKqyqQdgI1G6aJ3ag7eMCyRHXp7TzVjDqB_gs"

    tiles = [
        (15,7358,14580),
        (15,7358,14581)
    ]

    layers = ["default", "transit", "advanced_pois", "advanced_roads", "hillshade"]

    client = HEREVectorTileClient(API_KEY)

    for z,x,y in tiles:
        print(f"\n{'='*60}")
        print(f"[瓦片] Z={z}, X={x}, Y={y}")
        print(f"{'='*60}")

        data = client.request_tile(z, x, y, layers)

        if data:
            client.save_raw(data, z, x, y)
            geojson = client.parse_to_geojson(data, z, x, y)
            if geojson:
                client.save_geojson(geojson)
                first = geojson.get("features", [None])[0]
                if first and first.get("geometry"):
                    gtype = first["geometry"].get("type")
                    coords = first["geometry"].get("coordinates", [])
                    preview = str(coords[0] if isinstance(coords[0], list) else coords)[:120]
                    print(f"   坐标示例 ({gtype}): {preview}")
            print(f"[完成]")
        else:
            print(f"[失败]")


if __name__ == "__main__":
    main()
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇