关于地理格式数据KML的各种转换

首先看下有没有你想要的格式,没有就不要往下看了,当然,大多数文件都可以转换,除非你的文件是宇宙航天级别的复杂度,纯手工制作,尊重原创哈(2024.7.29文章更新,更新图形化界面及相应算法【cloud转JSON部分为客户定制内容,未公布】,以下文章内容为最新版本2.0)。

在这里插入图片描述

一、先看最终效果

这次就不放置源码文件了哈,有需要可以在文章下边复制,最终留给大家的是一个打包好的exe程序,见下图:
在这里插入图片描述

1. 双击运行后出现主界面

主界面可以选择文件、选择输出的格式、选择文件输出的位置,暂不支持批量操作哈,若有需要还麻烦多点几次,就可以了,当然,如果你的文件量巨大,随时可以通过文章末尾联系,支持定制。
在这里插入图片描述

2. 选择文件界面

注意,新版本升级后我添加了cloud文件选项,目前cloud文件仅支持转为JSON格式,且是一位朋友定制的需求(cloud转json不是通用的哈,慎用!!!)
在这里插入图片描述

3. 选择文件输出格式

文件的输出格式目前支持:JSON、XML、GeoGebra、CSV、Shapefile、GPX、WKT,一般来说标准的文件转换都不成问题哈,如果是定制的,建议研究下代码自行升级一下我给的算法。
在这里插入图片描述

4. 点击转换

这一步就不截图了,点击转换后就可以在指定的目录下边找到一个和目标文件同名的文件,但是格式已经换成了选择的输出格式。

二、关于源码

1. 核心算法说明

这里是类型转换的核心代码,若有需要可以研究下这里:

    def convert_kml_to_json(self):
        import xml.etree.ElementTree as ET
        import json

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个空的字典来存储 JSON 数据
        json_data = {'placemarks': []}

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            placemark_data = {}

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            if name is not None:
                placemark_data['name'] = name.text

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            if description is not None:
                placemark_data['description'] = description.text

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = coords_text.split(',')
                placemark_data['coordinates'] = {
                    'longitude': float(coords[0]),
                    'latitude': float(coords[1]),
                    'altitude': float(coords[2]) if len(coords) > 2 else 0.0
                }

            json_data['placemarks'].append(placemark_data)

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.json')

        # 将数据写入 JSON 文件
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(json_data, f, ensure_ascii=False, indent=4)

        QMessageBox.information(self, "提示", "KML文件已成功转换为JSON")

    def convert_kml_to_xml(self):
        import xml.etree.ElementTree as ET

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 创建一个新的 XML 结构
        new_root = ET.Element('ConvertedData')
        placemarks_element = ET.SubElement(new_root, 'Placemarks')

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            placemark_element = ET.SubElement(placemarks_element, 'Placemark')

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            if name is not None:
                name_element = ET.SubElement(placemark_element, 'Name')
                name_element.text = name.text

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            if description is not None:
                description_element = ET.SubElement(placemark_element, 'Description')
                description_element.text = description.text

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = coords_text.split(',')
                coordinates_element = ET.SubElement(placemark_element, 'Coordinates')
                longitude_element = ET.SubElement(coordinates_element, 'Longitude')
                latitude_element = ET.SubElement(coordinates_element, 'Latitude')
                altitude_element = ET.SubElement(coordinates_element, 'Altitude')

                longitude_element.text = coords[0]
                latitude_element.text = coords[1]
                altitude_element.text = coords[2] if len(coords) > 2 else '0.0'

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '_converted.xml')

        # 将新的 XML 结构写入文件
        new_tree = ET.ElementTree(new_root)
        new_tree.write(output_file, encoding='utf-8', xml_declaration=True)

        QMessageBox.information(self, "提示", "KML文件已成功转换为XML")

    def convert_kml_to_geojson(self):
        import xml.etree.ElementTree as ET
        import json

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个空的字典来存储 GeoJSON 数据
        geojson_data = {
            'type': 'FeatureCollection',
            'features': []
        }

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            feature = {
                'type': 'Feature',
                'geometry': {
                    'type': 'Point',
                    'coordinates': []
                },
                'properties': {}
            }

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            if name is not None:
                feature['properties']['name'] = name.text

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            if description is not None:
                feature['properties']['description'] = description.text

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = list(map(float, coords_text.split(',')))
                feature['geometry']['coordinates'] = coords

            geojson_data['features'].append(feature)

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.geojson')

        # 将数据写入 GeoJSON 文件
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(geojson_data, f, ensure_ascii=False, indent=4)

        QMessageBox.information(self, "提示", "KML文件已成功转换为GeoJSON")

    def convert_kml_to_csv(self):
        import xml.etree.ElementTree as ET
        import csv

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个列表来存储 CSV 数据
        csv_data = []

        # 添加 CSV 文件的头部
        csv_data.append(['Name', 'Description', 'Longitude', 'Latitude', 'Altitude'])

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            row = []

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            row.append(name.text if name is not None else '')

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            row.append(description.text if description is not None else '')

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = coords_text.split(',')
                row.append(coords[0])  # Longitude
                row.append(coords[1])  # Latitude
                row.append(coords[2] if len(coords) > 2 else '0.0')  # Altitude
            else:
                row.extend(['', '', ''])

            csv_data.append(row)

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.csv')

        # 将数据写入 CSV 文件
        with open(output_file, 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            writer.writerows(csv_data)

        QMessageBox.information(self, "提示", "KML文件已成功转换为CSV")

    def convert_kml_to_shapefile(self):
        import xml.etree.ElementTree as ET
        import shapefile

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个 Shapefile Writer 对象
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.shp')
        w = shapefile.Writer(output_file, shapefile.POINT)

        # 定义 Shapefile 的字段
        w.field('Name', 'C')
        w.field('Description', 'C')

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            name_text = name.text if name is not None else ''

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            description_text = description.text if description is not None else ''

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = list(map(float, coords_text.split(',')))
                longitude = coords[0]
                latitude = coords[1]

                # 写入 Shapefile
                w.point(longitude, latitude)
                w.record(name_text, description_text)

        # 关闭 Shapefile Writer
        w.close()

        # 如果需要生成附加的文件,如 .prj 文件
        prj_file = output_file.replace('.shp', '.prj')
        with open(prj_file, 'w') as prj:
            epsg_code = 'EPSG:4326'  # WGS 84
            prj.write(
                f'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]')

        QMessageBox.information(self, "提示", "KML文件已成功转换为Shapefile")

    def convert_kml_to_gpx(self):
        import xml.etree.ElementTree as ET

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建 GPX 根元素
        gpx = ET.Element('gpx', version="1.1", creator="KML to GPX Converter",
                         xmlns="http://www.topografix.com/GPX/1/1")

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            wpt = ET.SubElement(gpx, 'wpt')

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            if name is not None:
                ET.SubElement(wpt, 'name').text = name.text

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            if description is not None:
                ET.SubElement(wpt, 'desc').text = description.text

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = list(map(float, coords_text.split(',')))
                wpt.set('lon', str(coords[0]))
                wpt.set('lat', str(coords[1]))
                if len(coords) > 2:
                    wpt.set('ele', str(coords[2]))

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.gpx')

        # 将 GPX 数据写入文件
        tree = ET.ElementTree(gpx)
        tree.write(output_file, encoding='utf-8', xml_declaration=True)

        QMessageBox.information(self, "提示", "KML文件已成功转换为GPX")

    def convert_kml_to_wkt(self):
        import xml.etree.ElementTree as ET

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个空的列表来存储 WKT 数据
        wkt_data = []

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = coords_text.split(',')
                longitude = float(coords[0])
                latitude = float(coords[1])
                altitude = float(coords[2]) if len(coords) > 2 else 0.0

                # 创建 WKT 表示
                wkt = f'POINT ({longitude} {latitude} {altitude})'
                wkt_data.append(wkt)

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.wkt')

        # 将 WKT 数据写入文件
        with open(output_file, 'w', encoding='utf-8') as f:
            for wkt in wkt_data:
                f.write(wkt + '\n')

        QMessageBox.information(self, "提示", "KML文件已成功转换为WKT")

2. 完整源码(不包含cloud转JSON)

import sys
from cloud2json import Cloud2json
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QComboBox, QPushButton, QFileDialog, QMessageBox, QLineEdit, QSizePolicy, QHBoxLayout
from PyQt5.QtGui import QPalette, QBrush, QLinearGradient, QColor, QFont
from PyQt5.QtCore import Qt

import os
import json
import xml.etree.ElementTree as ET
import geopandas as gpd
import pandas as pd
from lxml import etree


class FileConverter(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setFixedSize(567, 380)
        self.setWindowTitle('文件转换器(2.0)-FifthDesign')

        # 设置渐变背景
        palette = QPalette()
        gradient = QLinearGradient(0, 0, 567, 380)
        gradient.setColorAt(0, QColor(135, 206, 250))
        gradient.setColorAt(1, QColor(100, 149, 237))  # 浅一点的深色
        palette.setBrush(QPalette.Window, QBrush(gradient))
        self.setPalette(palette)

        # 设置字体
        font = QFont("微软雅黑", 11)

        layout = QVBoxLayout()
        layout.setAlignment(Qt.AlignCenter)

        self.label_select_file = QLabel('选择文件:')
        self.label_select_file.setFont(font)
        layout.addWidget(self.label_select_file)

        self.file_layout = QHBoxLayout()
        self.file_layout.setAlignment(Qt.AlignCenter)
        self.file_preview = QLineEdit()
        self.file_preview.setFont(font)
        self.file_preview.setFixedHeight(30)  # 稍微低一点
        self.file_preview.setReadOnly(True)
        self.file_layout.addWidget(self.file_preview)
        self.btn_select_file = QPushButton('选择文件')
        self.btn_select_file.setFont(font)
        self.btn_select_file.setFixedSize(150, 40)
        self.btn_select_file.clicked.connect(self.select_file)
        self.file_layout.addWidget(self.btn_select_file)
        layout.addLayout(self.file_layout)
        layout.addSpacing(20)

        self.label_output_format = QLabel('选择输出格式:')
        self.label_output_format.setFont(font)
        layout.addWidget(self.label_output_format)

        self.combo_output_format = QComboBox()
        self.combo_output_format.setFont(font)
        self.combo_output_format.addItems(['JSON', 'XML', 'GeoJSON', 'CSV', 'Shapefile', 'GPX', 'WKT'])
        layout.addWidget(self.combo_output_format)
        layout.addSpacing(20)

        self.label_output_location = QLabel('选择文件输出位置:')
        self.label_output_location.setFont(font)
        layout.addWidget(self.label_output_location)

        self.output_layout = QHBoxLayout()
        self.output_layout.setAlignment(Qt.AlignCenter)
        self.output_preview = QLineEdit()
        self.output_preview.setFont(font)
        self.output_preview.setFixedHeight(30)  # 稍微低一点
        self.output_preview.setReadOnly(True)
        self.output_layout.addWidget(self.output_preview)
        self.btn_output_location = QPushButton('选择输出位置')
        self.btn_output_location.setFont(font)
        self.btn_output_location.setFixedSize(150, 40)
        self.btn_output_location.clicked.connect(self.select_output_location)
        self.output_layout.addWidget(self.btn_output_location)
        layout.addLayout(self.output_layout)
        layout.addSpacing(20)

        self.btn_convert = QPushButton('开始转换')
        self.btn_convert.setFont(font)
        self.btn_convert.setFixedSize(150, 40)
        self.btn_convert.clicked.connect(self.convert_file)
        layout.addWidget(self.btn_convert, alignment=Qt.AlignCenter)

        self.setLayout(layout)

        self.selected_file = None
        self.output_location = None

    def select_file(self):
        options = QFileDialog.Options()

        # 获取桌面路径
        desktop_path = os.path.expanduser("~/Desktop")
        file, _ = QFileDialog.getOpenFileName(self, "选择文件", desktop_path, "KML Files (*.kml);;CLOUD Files (*.cloud);;All Files (*)", options=options)
        if file:
            if not file.lower().endswith(('.kml', '.cloud')):
                QMessageBox.critical(self, "错误", "只支持KML和CLOUD文件")
            else:
                self.selected_file = file
                self.file_preview.setText(file)

    def select_output_location(self):
        options = QFileDialog.Options()
        folder = QFileDialog.getExistingDirectory(self, "选择文件输出位置", options=options)
        if folder:
            self.output_location = folder
            self.output_preview.setText(folder)

    def convert_file(self):
        if not self.selected_file:
            QMessageBox.critical(self, "错误", "请选择文件")
            return
        if not self.output_location:
            QMessageBox.critical(self, "错误", "请选择文件输出位置")
            return

        input_format = self.selected_file.split('.')[-1].lower()
        output_format = self.combo_output_format.currentText()

        if input_format == 'cloud' and output_format != 'JSON':
            QMessageBox.critical(self, "错误", "CLOUD文件只支持转换为JSON")
            return

        if input_format == 'kml':
            self.convert_kml(output_format)
        elif input_format == 'cloud':
            self.convert_cloud(output_format)

    def convert_kml(self, output_format):
        if output_format == 'JSON':
            self.convert_kml_to_json()
        elif output_format == 'XML':
            self.convert_kml_to_xml()
        elif output_format == 'GeoJSON':
            self.convert_kml_to_geojson()
        elif output_format == 'CSV':
            self.convert_kml_to_csv()
        elif output_format == 'Shapefile':
            self.convert_kml_to_shapefile()
        elif output_format == 'GPX':
            self.convert_kml_to_gpx()
        elif output_format == 'WKT':
            self.convert_kml_to_wkt()
        QMessageBox.information(self, "提示", f"KML文件已成功转换为{output_format}")

    def convert_cloud(self, output_format):
        # 这里写CLOUD转换为JSON的具体实现
        # print(self.selected_file)
        # print(self.output_location)
        # cloud2json = Cloud2json()
        # cloud2json.run(self.selected_file, self.output_location)

        QMessageBox.information(self, "提示", "CLOUD文件已成功转换为JSON")

    def convert_kml_to_json(self):
        import xml.etree.ElementTree as ET
        import json

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个空的字典来存储 JSON 数据
        json_data = {'placemarks': []}

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            placemark_data = {}

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            if name is not None:
                placemark_data['name'] = name.text

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            if description is not None:
                placemark_data['description'] = description.text

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = coords_text.split(',')
                placemark_data['coordinates'] = {
                    'longitude': float(coords[0]),
                    'latitude': float(coords[1]),
                    'altitude': float(coords[2]) if len(coords) > 2 else 0.0
                }

            json_data['placemarks'].append(placemark_data)

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.json')

        # 将数据写入 JSON 文件
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(json_data, f, ensure_ascii=False, indent=4)

        QMessageBox.information(self, "提示", "KML文件已成功转换为JSON")

    def convert_kml_to_xml(self):
        import xml.etree.ElementTree as ET

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 创建一个新的 XML 结构
        new_root = ET.Element('ConvertedData')
        placemarks_element = ET.SubElement(new_root, 'Placemarks')

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            placemark_element = ET.SubElement(placemarks_element, 'Placemark')

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            if name is not None:
                name_element = ET.SubElement(placemark_element, 'Name')
                name_element.text = name.text

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            if description is not None:
                description_element = ET.SubElement(placemark_element, 'Description')
                description_element.text = description.text

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = coords_text.split(',')
                coordinates_element = ET.SubElement(placemark_element, 'Coordinates')
                longitude_element = ET.SubElement(coordinates_element, 'Longitude')
                latitude_element = ET.SubElement(coordinates_element, 'Latitude')
                altitude_element = ET.SubElement(coordinates_element, 'Altitude')

                longitude_element.text = coords[0]
                latitude_element.text = coords[1]
                altitude_element.text = coords[2] if len(coords) > 2 else '0.0'

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '_converted.xml')

        # 将新的 XML 结构写入文件
        new_tree = ET.ElementTree(new_root)
        new_tree.write(output_file, encoding='utf-8', xml_declaration=True)

        QMessageBox.information(self, "提示", "KML文件已成功转换为XML")

    def convert_kml_to_geojson(self):
        import xml.etree.ElementTree as ET
        import json

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个空的字典来存储 GeoJSON 数据
        geojson_data = {
            'type': 'FeatureCollection',
            'features': []
        }

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            feature = {
                'type': 'Feature',
                'geometry': {
                    'type': 'Point',
                    'coordinates': []
                },
                'properties': {}
            }

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            if name is not None:
                feature['properties']['name'] = name.text

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            if description is not None:
                feature['properties']['description'] = description.text

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = list(map(float, coords_text.split(',')))
                feature['geometry']['coordinates'] = coords

            geojson_data['features'].append(feature)

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.geojson')

        # 将数据写入 GeoJSON 文件
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(geojson_data, f, ensure_ascii=False, indent=4)

        QMessageBox.information(self, "提示", "KML文件已成功转换为GeoJSON")

    def convert_kml_to_csv(self):
        import xml.etree.ElementTree as ET
        import csv

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个列表来存储 CSV 数据
        csv_data = []

        # 添加 CSV 文件的头部
        csv_data.append(['Name', 'Description', 'Longitude', 'Latitude', 'Altitude'])

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            row = []

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            row.append(name.text if name is not None else '')

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            row.append(description.text if description is not None else '')

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = coords_text.split(',')
                row.append(coords[0])  # Longitude
                row.append(coords[1])  # Latitude
                row.append(coords[2] if len(coords) > 2 else '0.0')  # Altitude
            else:
                row.extend(['', '', ''])

            csv_data.append(row)

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.csv')

        # 将数据写入 CSV 文件
        with open(output_file, 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            writer.writerows(csv_data)

        QMessageBox.information(self, "提示", "KML文件已成功转换为CSV")

    def convert_kml_to_shapefile(self):
        import xml.etree.ElementTree as ET
        import shapefile

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个 Shapefile Writer 对象
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.shp')
        w = shapefile.Writer(output_file, shapefile.POINT)

        # 定义 Shapefile 的字段
        w.field('Name', 'C')
        w.field('Description', 'C')

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            name_text = name.text if name is not None else ''

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            description_text = description.text if description is not None else ''

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = list(map(float, coords_text.split(',')))
                longitude = coords[0]
                latitude = coords[1]

                # 写入 Shapefile
                w.point(longitude, latitude)
                w.record(name_text, description_text)

        # 关闭 Shapefile Writer
        w.close()

        # 如果需要生成附加的文件,如 .prj 文件
        prj_file = output_file.replace('.shp', '.prj')
        with open(prj_file, 'w') as prj:
            epsg_code = 'EPSG:4326'  # WGS 84
            prj.write(
                f'GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]')

        QMessageBox.information(self, "提示", "KML文件已成功转换为Shapefile")

    def convert_kml_to_gpx(self):
        import xml.etree.ElementTree as ET

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建 GPX 根元素
        gpx = ET.Element('gpx', version="1.1", creator="KML to GPX Converter",
                         xmlns="http://www.topografix.com/GPX/1/1")

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            wpt = ET.SubElement(gpx, 'wpt')

            # 获取 Placemark 名称
            name = placemark.find('kml:name', namespaces)
            if name is not None:
                ET.SubElement(wpt, 'name').text = name.text

            # 获取 Placemark 描述
            description = placemark.find('kml:description', namespaces)
            if description is not None:
                ET.SubElement(wpt, 'desc').text = description.text

            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = list(map(float, coords_text.split(',')))
                wpt.set('lon', str(coords[0]))
                wpt.set('lat', str(coords[1]))
                if len(coords) > 2:
                    wpt.set('ele', str(coords[2]))

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.gpx')

        # 将 GPX 数据写入文件
        tree = ET.ElementTree(gpx)
        tree.write(output_file, encoding='utf-8', xml_declaration=True)

        QMessageBox.information(self, "提示", "KML文件已成功转换为GPX")

    def convert_kml_to_wkt(self):
        import xml.etree.ElementTree as ET

        # 读取 KML 文件
        tree = ET.parse(self.selected_file)
        root = tree.getroot()

        # 定义命名空间
        namespaces = {'kml': 'http://www.opengis.net/kml/2.2'}

        # 创建一个空的列表来存储 WKT 数据
        wkt_data = []

        # 遍历 KML 文件中的所有 Placemark
        for placemark in root.findall('.//kml:Placemark', namespaces):
            # 获取 Placemark 的坐标
            coordinates = placemark.find('.//kml:coordinates', namespaces)
            if coordinates is not None:
                coords_text = coordinates.text.strip()
                coords = coords_text.split(',')
                longitude = float(coords[0])
                latitude = float(coords[1])
                altitude = float(coords[2]) if len(coords) > 2 else 0.0

                # 创建 WKT 表示
                wkt = f'POINT ({longitude} {latitude} {altitude})'
                wkt_data.append(wkt)

        # 生成输出文件路径
        output_file = os.path.join(self.output_location,
                                   os.path.splitext(os.path.basename(self.selected_file))[0] + '.wkt')

        # 将 WKT 数据写入文件
        with open(output_file, 'w', encoding='utf-8') as f:
            for wkt in wkt_data:
                f.write(wkt + '\n')

        QMessageBox.information(self, "提示", "KML文件已成功转换为WKT")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    converter = FileConverter()
    converter.show()
    sys.exit(app.exec_())

3. 简要说明

  1. 程序采用Pyqt5进行图形化界面编写、采用python 11环境,库的话运行的时候缺啥安装啥就可以,问题不大。
  2. 有问题欢迎通过文末渠道联系,exe程序的话关注后直接回复“ZD002”就可以了。
  3. 注意,转换代码只是我工作需要临时编码了一下,完成了自己的目的,并没有完备的测试,你转换后记得测试一下文件正确性哈。

三、文章最后

欢迎关注微信公众号“第五智能”,代码+设计,让我们走在时代前沿,若有任何问题随时咨询。若需本文资料只需回复“ZD002即可”。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐