在处理地理空间数据时,KMZ 文件是一种常见的格式,用于存储地图和地理信息数据。KMZ 文件是 KML 文件的压缩版本,其中 KML(Keyhole Markup Language)用于描述地理数据的格式。本文将详细介绍如何使用 Python 处理 KMZ 文件,提取其中的地理数据,并将其可视化到地图上。本文的核心代码将涉及文件的解压、KML 文件的解析、GPS 数据的提取以及如何使用 Folium 库将数据展示到地图上。

1. KMZ 文件与 KML 文件简介

1.1 KMZ 文件

KMZ 文件是 KML 文件的压缩版本,通常用于存储 Google Earth 或 Google Maps 中使用的地理信息数据。KMZ 文件可以包含一个或多个 KML 文件以及其他资源文件(如图片、图标等)。KML 文件是基于 XML 的格式,用于描述地理数据的标记、路径、区域等信息。

1.2 KML 文件

KML 文件由 XML 构成,用于存储地理数据,如地点标记、线条、区域、图像叠加等。KML 的基本结构包括:

  • Placemark:标记点
  • Point:点类型
  • LineString:线条
  • Polygon:多边形
  • TimeStamp:时间戳

2. Python 环境配置与依赖安装

在开始之前,确保你的 Python 环境中已经安装了以下依赖:

  • folium:用于地图可视化
  • xml.etree.ElementTree:用于解析 XML 文件
  • zipfile:用于解压 KMZ 文件
  • glob:用于文件路径匹配

可以使用以下命令安装所需的库:

pip install folium

3. 代码实现详解

3.1 查找 KMZ 文件
import os
import glob

def find_kmz_files(directory):
    # 使用 glob 模块查找指定目录下的所有 .kmz 文件
    kmz_files = glob.glob(os.path.join(directory, '*.kmz'))
    return kmz_files
  • 功能:遍历指定目录,查找所有以 .kmz 结尾的文件。
  • 实现:使用 glob 模块和通配符模式来匹配所有 KMZ 文件。
3.2 解压 KMZ 文件
import zipfile

def extract_kml_from_kmz(kmz_file_path):
    # 解压 KMZ 文件
    with zipfile.ZipFile(kmz_file_path, 'r') as kmz:
        # 查找 KML 文件
        kml_files = [name for name in kmz.namelist() if name.lower().endswith('.kml')]
        if kml_files:
            kml_file_path = kml_files[0]
            kmz.extract(kml_file_path, os.path.dirname(kmz_file_path))
            return os.path.join(os.path.dirname(kmz_file_path), kml_file_path)
    return None
  • 功能:解压 KMZ 文件,并提取其中的 KML 文件。
  • 实现:使用 zipfile 模块打开 KMZ 文件,查找并解压 KML 文件。
3.3 解析 KML 文件
import xml.etree.ElementTree as ET

def parse_kml(kml_file_path):
    gps_data = []
    tree = ET.parse(kml_file_path)
    root = tree.getroot()
    
    # KML 的 XML namespace
    namespace = {'kml': 'http://earth.google.com/kml/2.2'}
    print(f"Root element: {root.tag}")
    
    # 查找所有 Placemark 元素
    for placemark in root.findall('.//kml:Placemark', namespace):
        coordinates = placemark.find('.//kml:Point/kml:coordinates', namespace)
        
        if coordinates is not None:
            coords = coordinates.text.strip().split(',')
            if len(coords) >= 3:
                try:
                    longitude = float(coords[0])
                    latitude = float(coords[1])
                    altitude = float(coords[2])
                    gps_data.append({
                        'latitude': latitude,
                        'longitude': longitude,
                        'altitude': altitude
                    })
                except ValueError as e:
                    print(f"Error parsing coordinates: {e}")
    return gps_data
  • 功能:解析 KML 文件,提取 GPS 数据(经纬度和高度)。
  • 实现:使用 xml.etree.ElementTree 解析 XML 格式的 KML 文件,通过查找 Placemark 元素和 coordinates 元素来获取地理数据。
3.4 可视化 KMZ 数据
import folium
from folium.features import CustomIcon

def visualize_multiple_kmz_data(kmz_data_list):
    if not kmz_data_list:
        print("No GPS data available to visualize.")
        return

    # 计算所有经纬度的平均值,作为地图的中心
    all_latitudes = []
    all_longitudes = []
    
    for kmz_data in kmz_data_list:
        latitudes = [data['latitude'] for data in kmz_data['gps_data']]
        longitudes = [data['longitude'] for data in kmz_data['gps_data']]
        all_latitudes.extend(latitudes)
        all_longitudes.extend(longitudes)

    avg_latitude = sum(all_latitudes) / len(all_latitudes)
    avg_longitude = sum(all_longitudes) / len(all_longitudes)
    
    map_center = [avg_latitude, avg_longitude]
    
    gps_map = folium.Map(location=map_center, zoom_start=14, tiles='OpenStreetMap')

    folium.TileLayer(
        tiles='https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
        name='Google Satellite',
        attr='© Google'
    ).add_to(gps_map)

    folium.LayerControl().add_to(gps_map)

    # 为每个 KMZ 文件使用不同的颜色
    colors = ['red', 'blue', 'green', 'purple', 'orange', 'darkred', 'lightred', 
              'beige', 'darkblue', 'darkgreen', 'cadetblue', 'darkpurple', 'white', 
              'pink', 'lightblue', 'lightgreen', 'gray', 'black', 'lightgray']

    for idx, kmz_data in enumerate(kmz_data_list):
        color = colors[idx % len(colors)]
        
        for data in kmz_data['gps_data']:
            folium.CircleMarker(
                [data['latitude'], data['longitude']],
                radius=0.5,  # 半径大小
                color=color,  # 边框颜色
                fill=True,
                fill_color=color,  # 填充颜色
                fill_opacity=0.8
            ).add_to(gps_map)
        
        # 绘制路径线并添加箭头
        for i in range(1, len(kmz_data['gps_data'])):
            start_point = kmz_data['gps_data'][i-1]
            end_point = kmz_data['gps_data'][i]

            # 绘制线条
            folium.PolyLine(
                locations=[(start_point['latitude'], start_point['longitude']),
                           (end_point['latitude'], end_point['longitude'])],
                color=color,
                weight=2
            ).add_to(gps_map)

            # 添加箭头
            folium.Marker(
                location=[(start_point['latitude'] + end_point['latitude']) / 2,
                          (start_point['longitude'] + end_point['longitude']) / 2],
                icon=CustomIcon('https://upload.wikimedia.org/wikipedia/commons/e/e5/Black_triangle_pointing_right.svg',
                                icon_size=(10, 10), icon_anchor=(5, 5))
            ).add_to(gps_map)
        
    gps_map.save('multiple_kmz_map.html')
    print("GPS map saved as 'multiple_kmz_map.html'.")
  • 功能:将多个 KMZ 文件的数据可视化到一个地图上,使用不同的颜色表示不同的 KMZ 文件。
  • 实现
    • 计算所有点的平均经纬度作为地图的中心。
    • 使用 folium.Map 创建地图,并添加地图图层。
    • 对每个 KMZ 文件使用不同的颜色,并将其 GPS 数据以 CircleMarker 的形式添加到地图上。

绘制路径线,并在路径中添加箭头指示方向。

4. 项目实战

4.1. 数据采集

两个kmz文件:
在这里插入图片描述
其中一个kmz文件解压,会看到有一个kml文件:
在这里插入图片描述
kml文件打开,会看到一些关键信息,以下是部分信息截图:
在这里插入图片描述

4.2. 项目完整代码
import os
import glob
import folium
import zipfile
import xml.etree.ElementTree as ET
from folium.features import CustomIcon

def find_kmz_files(directory):
    kmz_files = glob.glob(os.path.join(directory, '*.kmz'))
    return kmz_files

def extract_kml_from_kmz(kmz_file_path):
    with zipfile.ZipFile(kmz_file_path, 'r') as kmz:
        kml_files = [name for name in kmz.namelist() if name.lower().endswith('.kml')]
        if kml_files:
            kml_file_path = kml_files[0]
            kmz.extract(kml_file_path, os.path.dirname(kmz_file_path))
            return os.path.join(os.path.dirname(kmz_file_path), kml_file_path)
    return None

def parse_kml(kml_file_path):
    gps_data = []
    tree = ET.parse(kml_file_path)
    root = tree.getroot()
    namespace = {'kml': 'http://earth.google.com/kml/2.2'}
    
    for placemark in root.findall('.//kml:Placemark', namespace):
        coordinates = placemark.find('.//kml:Point/kml:coordinates', namespace)
        
        if coordinates is not None:
            coords = coordinates.text.strip().split(',')
            if len(coords) >= 3:
                try:
                    longitude = float(coords[0])
                    latitude = float(coords[1])
                    altitude = float(coords[2])
                    gps_data.append({
                        'latitude': latitude,
                        'longitude': longitude,
                        'altitude': altitude
                    })
                except ValueError as e:
                    print(f"Error parsing coordinates: {e}")
    return gps_data

def visualize_multiple_kmz_data(kmz_data_list):
    if not kmz_data_list:
        print("No GPS data available to visualize.")
        return

    all_latitudes = []
    all_longitudes = []
    
    for kmz_data in kmz_data_list:
        latitudes = [data['latitude'] for data in kmz_data['gps_data']]
        longitudes = [data['longitude'] for data in kmz_data['gps_data']]
        all_latitudes.extend(latitudes)
        all_longitudes.extend(longitudes)

    avg_latitude = sum(all_latitudes) / len(all_latitudes)
    avg_longitude = sum(all_longitudes) / len(all_longitudes)
    
    map_center = [avg_latitude, avg_longitude]
    
    gps_map = folium.Map(location=map_center, zoom_start=14, tiles='OpenStreetMap')

    folium.TileLayer(
        tiles='https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
        name='Google Satellite',
        attr='© Google'
    ).add_to(gps_map)

    folium.LayerControl().add_to(gps_map)

    colors = ['red', 'blue', 'green', 'purple', 'orange', 'darkred', 'lightred', 
              'beige', 'darkblue', 'darkgreen', 'cadetblue', 'darkpurple', 'white', 
              'pink', 'lightblue', 'lightgreen', 'gray', 'black', 'lightgray']

    for idx, kmz_data in enumerate(kmz_data_list):
        color = colors[idx % len(colors)]
        
        for data in kmz_data['gps_data']:
            folium.CircleMarker(
                [data['latitude'], data['longitude']],
                radius=0.5,
                color=color,
                fill=True,
                fill_color=color,
                fill_opacity=0.8
            ).add_to(gps_map)
        
        for i in range(1, len(kmz_data['gps_data'])):
            start_point = kmz_data['gps_data'][i-1]
            end_point = kmz_data['gps_data'][i]

            folium.PolyLine(
                locations=[(start_point['latitude'], start_point['longitude']),
                           (end_point['latitude'], end_point['longitude'])],
                color=color,
                weight=2
            ).add_to(gps_map)

            folium.Marker(
                location=[(start_point['latitude'] + end_point['latitude']) / 2,
                          (start_point['longitude'] + end_point['longitude']) / 2],
                icon=CustomIcon('https://upload.wikimedia.org/wikipedia/commons/e/e5/Black_triangle_pointing_right.svg',
                                icon_size=(10, 10), icon_anchor=(5, 5))
            ).add_to(gps_map)
        
    gps_map.save('multiple_kmz_map.html')
    print("GPS map saved as 'multiple_kmz_map.html'.")

if __name__ == '__main__':
    directory_path = "F:\\notebookComputer\\20240723"
    
    kmz_files = find_kmz_files(directory_path)
    
    if kmz_files:
        kmz_data_list = []
        for kmz_file_path in kmz_files:
            kml_file_path = extract_kml_from_kmz(kmz_file_path)
            if kml_file_path:
                parsed_gps_data = parse_kml(kml_file_path)
                print(f"Parsed GPS data: {parsed_gps_data}")
                kmz_data_list.append({
                    'file_name': os.path.basename(kmz_file_path),
                    'gps_data': parsed_gps_data
                })
        
        if kmz_data_list:
            visualize_multiple_kmz_data(kmz_data_list)
        else:
            print("No GPS data available to visualize.")
    else:
        print(f"No .kmz files found in directory: {directory_path}")

5. 项目运行与结果展示

在代码执行完毕后,将会生成一个名为 multiple_kmz_map.html 的文件,该文件可以用浏览器打开以查看地图上的标记点和路径。地图将会显示所有 KMZ 文件中提取的 GPS 数据,每个文件的标记点使用不同的颜色表示。
multiple_kmz_map.html文件不好截图如下:
在这里插入图片描述
浏览器打开multiple_kmz_map.html文件效果图如下:
在这里插入图片描述

6. 总结与展望

本文详细介绍了如何使用 Python 处理 KMZ 文件,提取其中的 GPS 数据,并通过 Folium 库将其可视化。通过将 KMZ 文件中的地理数据转换为地图标记点和路径线,我们可以更直观地分析和展示地理数据。未来的工作可以包括支持更多的地理数据格式、添加更多的地图样式和功能、以及优化代码的性能和可读性。根据需求,文章可以继续扩展,以包含更多的技术细节、优化建议和实际应用场景的分析。

欢迎点赞|关注|收藏|评论,您的肯定是我创作的动力

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐