KMZ文件处理与可视化深度解析:数据提取到地图展示的全流程实战指南

文章目录

  • 1. KMZ 文件与 KML 文件简介
  • 1.1 KMZ 文件
  • 1.2 KML 文件
  • 2. Python 环境配置与依赖安装
  • 3. 代码实现详解
  • 3.1 查找 KMZ 文件
  • 3.2 解压 KMZ 文件
  • 3.3 解析 KML 文件
  • 3.4 可视化 KMZ 数据
  • 4. 项目实战
  • 4.1. 数据采集
  • 4.2. 项目完整代码
  • 5. 项目运行与结果展示
  • 6. 总结与展望
  • 在处理地理空间数据时,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 文件中的地理数据转换为地图标记点和路径线,我们可以更直观地分析和展示地理数据。未来的工作可以包括支持更多的地理数据格式、添加更多的地图样式和功能、以及优化代码的性能和可读性。根据需求,文章可以继续扩展,以包含更多的技术细节、优化建议和实际应用场景的分析。

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

    作者:邓瑞军说HelloWorld

    物联沃分享整理
    物联沃-IOTWORD物联网 » KMZ文件处理与可视化深度解析:数据提取到地图展示的全流程实战指南

    发表回复