Created
March 19, 2025 18:41
-
-
Save dinizime/20b9d0add1793bce7464661c9e3775bf to your computer and use it in GitHub Desktop.
Converte camada poligono do qgis para kmz com label
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from qgis.core import QgsProject, QgsGeometry, QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsWkbTypes, QgsPointXY | |
| import os | |
| import zipfile | |
| import xml.dom.minidom as minidom | |
| import random | |
| import math | |
| def create_kmz_with_labels(layer_name, label_field, output_kmz_path, | |
| line_color='ff4aaf4d', line_width=4, | |
| label_color='ff00ff00', label_scale=1.5, | |
| min_lod_pixels=100, | |
| char_width_factor=0): # Fator de largura do caractere em coordenadas | |
| """ | |
| Cria um arquivo KMZ com polígonos e labels centralizados com controle de zoom | |
| Parâmetros: | |
| layer_name (str): Nome da camada no QGIS | |
| label_field (str): Nome do campo a ser usado como label | |
| output_kmz_path (str): Caminho completo para o arquivo KMZ de saída | |
| line_color (str): Cor da linha no formato ABGR (hexadecimal) | |
| line_width (int): Espessura da linha do polígono | |
| label_color (str): Cor do texto no formato ABGR (hexadecimal) | |
| label_scale (float): Tamanho do texto (1.0 = tamanho normal) | |
| min_lod_pixels (int): Nível de zoom mínimo para o texto aparecer | |
| char_width_factor (float): Fator para estimar a largura de um caractere em coordenadas geográficas | |
| """ | |
| # Obter a camada pelo nome | |
| layer = None | |
| for lyr in QgsProject.instance().mapLayers().values(): | |
| if lyr.name() == layer_name: | |
| layer = lyr | |
| break | |
| if not layer: | |
| print(f"Camada '{layer_name}' não encontrada!") | |
| return | |
| # Verificar se o campo existe | |
| field_index = layer.fields().indexFromName(label_field) | |
| if field_index == -1: | |
| print(f"Campo '{label_field}' não encontrado na camada!") | |
| return | |
| # Criar o documento KML | |
| doc = minidom.getDOMImplementation().createDocument(None, "kml", None) | |
| kml = doc.documentElement | |
| kml.setAttribute("xmlns", "http://www.opengis.net/kml/2.2") | |
| document = doc.createElement("Document") | |
| kml.appendChild(document) | |
| # Criar estilos | |
| # Estilo para polígonos | |
| polygon_style_id = f"polyStyle_{random.randint(1000, 9999)}" | |
| polygon_style = doc.createElement("Style") | |
| polygon_style.setAttribute("id", polygon_style_id) | |
| line_style = doc.createElement("LineStyle") | |
| color_element = doc.createElement("color") | |
| color_element.appendChild(doc.createTextNode(line_color)) | |
| line_style.appendChild(color_element) | |
| width_element = doc.createElement("width") | |
| width_element.appendChild(doc.createTextNode(str(line_width))) | |
| line_style.appendChild(width_element) | |
| polygon_style.appendChild(line_style) | |
| poly_style = doc.createElement("PolyStyle") | |
| fill_color = doc.createElement("color") | |
| fill_color.appendChild(doc.createTextNode("00ffffff")) # Transparente | |
| poly_style.appendChild(fill_color) | |
| fill_element = doc.createElement("fill") | |
| fill_element.appendChild(doc.createTextNode("1")) | |
| poly_style.appendChild(fill_element) | |
| outline_element = doc.createElement("outline") | |
| outline_element.appendChild(doc.createTextNode("1")) | |
| poly_style.appendChild(outline_element) | |
| polygon_style.appendChild(poly_style) | |
| document.appendChild(polygon_style) | |
| # Estilo para labels | |
| label_style_id = f"labelStyle_{random.randint(1000, 9999)}" | |
| label_style = doc.createElement("Style") | |
| label_style.setAttribute("id", label_style_id) | |
| icon_style = doc.createElement("IconStyle") | |
| icon_scale = doc.createElement("scale") | |
| icon_scale.appendChild(doc.createTextNode("0")) # Esconde o ícone | |
| icon_style.appendChild(icon_scale) | |
| label_style.appendChild(icon_style) | |
| label_style_element = doc.createElement("LabelStyle") | |
| label_color_element = doc.createElement("color") | |
| label_color_element.appendChild(doc.createTextNode(label_color)) | |
| label_style_element.appendChild(label_color_element) | |
| label_scale_element = doc.createElement("scale") | |
| label_scale_element.appendChild(doc.createTextNode(str(label_scale))) | |
| label_style_element.appendChild(label_scale_element) | |
| label_style.appendChild(label_style_element) | |
| document.appendChild(label_style) | |
| # Transformação para coordenadas geográficas (se necessário) | |
| src_crs = layer.crs() | |
| dest_crs = QgsCoordinateReferenceSystem("EPSG:4326") # WGS 84 | |
| transform = QgsCoordinateTransform(src_crs, dest_crs, QgsProject.instance()) | |
| # Processar cada feição | |
| for feature in layer.getFeatures(): | |
| # Obter o valor do campo para o label | |
| label_value = str(feature[label_field]) | |
| # Obter a geometria e transformar para WGS 84 | |
| geom = feature.geometry() | |
| geom.transform(transform) | |
| # Calcular o centroide exato do polígono | |
| centroid = geom.centroid().asPoint() | |
| # Criar placemark para o polígono | |
| poly_placemark = doc.createElement("Placemark") | |
| # Adicionar nome como atributo ao polígono | |
| poly_name = doc.createElement("name") | |
| poly_name.appendChild(doc.createTextNode(label_value)) | |
| poly_placemark.appendChild(poly_name) | |
| # Adicionar descrição com o valor do atributo | |
| poly_description = doc.createElement("description") | |
| poly_description.appendChild(doc.createTextNode('')) | |
| poly_placemark.appendChild(poly_description) | |
| style_url = doc.createElement("styleUrl") | |
| style_url.appendChild(doc.createTextNode(f"#{polygon_style_id}")) | |
| poly_placemark.appendChild(style_url) | |
| # Converter geometria para KML | |
| polygon_element = create_polygon_element(doc, geom) | |
| poly_placemark.appendChild(polygon_element) | |
| document.appendChild(poly_placemark) | |
| # Calcular deslocamento para centralizar o texto | |
| # Estimamos a largura do texto com base no número de caracteres | |
| # e aplicamos um deslocamento para a esquerda | |
| text_width = len(label_value) * char_width_factor * label_scale | |
| # O deslocamento é aplicado na direção oeste-leste (longitude) | |
| # Deslocamos para a esquerda metade da largura estimada | |
| offset_x = -text_width / 2 | |
| # Calculamos o novo ponto deslocado | |
| adjusted_x = centroid.x() + offset_x | |
| # Criar placemark para o label com controle de zoom | |
| label_placemark = doc.createElement("Placemark") | |
| label_name = doc.createElement("name") | |
| label_name.appendChild(doc.createTextNode(label_value)) | |
| label_placemark.appendChild(label_name) | |
| label_style_url = doc.createElement("styleUrl") | |
| label_style_url.appendChild(doc.createTextNode(f"#{label_style_id}")) | |
| label_placemark.appendChild(label_style_url) | |
| # Adicionar controle de zoom para o label | |
| region = doc.createElement("Region") | |
| lat_lon_alt_box = doc.createElement("LatLonAltBox") | |
| # Obter o envelope da geometria para o controle de região | |
| bbox = geom.boundingBox() | |
| north = doc.createElement("north") | |
| north.appendChild(doc.createTextNode(str(bbox.yMaximum()))) | |
| lat_lon_alt_box.appendChild(north) | |
| south = doc.createElement("south") | |
| south.appendChild(doc.createTextNode(str(bbox.yMinimum()))) | |
| lat_lon_alt_box.appendChild(south) | |
| east = doc.createElement("east") | |
| east.appendChild(doc.createTextNode(str(bbox.xMaximum()))) | |
| lat_lon_alt_box.appendChild(east) | |
| west = doc.createElement("west") | |
| west.appendChild(doc.createTextNode(str(bbox.xMinimum()))) | |
| lat_lon_alt_box.appendChild(west) | |
| region.appendChild(lat_lon_alt_box) | |
| lod = doc.createElement("Lod") | |
| min_lod = doc.createElement("minLodPixels") | |
| min_lod.appendChild(doc.createTextNode(str(min_lod_pixels))) | |
| lod.appendChild(min_lod) | |
| max_lod = doc.createElement("maxLodPixels") | |
| max_lod.appendChild(doc.createTextNode("-1")) # Sem limite | |
| lod.appendChild(max_lod) | |
| region.appendChild(lod) | |
| label_placemark.appendChild(region) | |
| # Adicionar ponto no centroide ajustado | |
| point = doc.createElement("Point") | |
| coords = doc.createElement("coordinates") | |
| coords.appendChild(doc.createTextNode(f"{adjusted_x},{centroid.y()},0")) | |
| point.appendChild(coords) | |
| label_placemark.appendChild(point) | |
| document.appendChild(label_placemark) | |
| # Salvar o KML temporário | |
| kml_temp_path = output_kmz_path.replace(".kmz", ".kml") | |
| with open(kml_temp_path, "w", encoding="utf-8") as f: | |
| f.write(doc.toprettyxml(indent=" ")) | |
| # Criar o arquivo KMZ (KML comprimido) | |
| with zipfile.ZipFile(output_kmz_path, 'w', zipfile.ZIP_DEFLATED) as zipf: | |
| zipf.write(kml_temp_path, os.path.basename(kml_temp_path)) | |
| # Remover o KML temporário | |
| os.remove(kml_temp_path) | |
| print(f"Arquivo KMZ criado com sucesso: {output_kmz_path}") | |
| def create_polygon_element(doc, geometry): | |
| """ | |
| Cria o elemento Polygon do KML a partir de uma geometria QgsGeometry | |
| """ | |
| # Usar o tipo correto de QgsWkbTypes para verificar o tipo de geometria | |
| geom_type = QgsWkbTypes.geometryType(geometry.wkbType()) | |
| if geom_type == QgsWkbTypes.PolygonGeometry: | |
| # Verificar se é um único polígono ou multipolígono | |
| if geometry.isMultipart(): | |
| # Para multipolígonos | |
| multi_geom = doc.createElement("MultiGeometry") | |
| multi_polygon = geometry.asMultiPolygon() | |
| for polygon in multi_polygon: | |
| poly = doc.createElement("Polygon") | |
| outer_boundary = doc.createElement("outerBoundaryIs") | |
| linear_ring = doc.createElement("LinearRing") | |
| coords = doc.createElement("coordinates") | |
| coord_str = " ".join([f"{point.x()},{point.y()},0" for point in polygon[0]]) | |
| coords.appendChild(doc.createTextNode(coord_str)) | |
| linear_ring.appendChild(coords) | |
| outer_boundary.appendChild(linear_ring) | |
| poly.appendChild(outer_boundary) | |
| # Adicionar buracos, se houver | |
| for i in range(1, len(polygon)): | |
| inner_boundary = doc.createElement("innerBoundaryIs") | |
| inner_ring = doc.createElement("LinearRing") | |
| inner_coords = doc.createElement("coordinates") | |
| inner_coord_str = " ".join([f"{point.x()},{point.y()},0" for point in polygon[i]]) | |
| inner_coords.appendChild(doc.createTextNode(inner_coord_str)) | |
| inner_ring.appendChild(inner_coords) | |
| inner_boundary.appendChild(inner_ring) | |
| poly.appendChild(inner_boundary) | |
| multi_geom.appendChild(poly) | |
| return multi_geom | |
| else: | |
| # Para polígonos simples | |
| poly = doc.createElement("Polygon") | |
| outer_boundary = doc.createElement("outerBoundaryIs") | |
| linear_ring = doc.createElement("LinearRing") | |
| coords = doc.createElement("coordinates") | |
| polygon = geometry.asPolygon() | |
| if polygon: | |
| coord_str = " ".join([f"{point.x()},{point.y()},0" for point in polygon[0]]) | |
| coords.appendChild(doc.createTextNode(coord_str)) | |
| linear_ring.appendChild(coords) | |
| outer_boundary.appendChild(linear_ring) | |
| poly.appendChild(outer_boundary) | |
| # Adicionar buracos, se houver | |
| for i in range(1, len(polygon)): | |
| inner_boundary = doc.createElement("innerBoundaryIs") | |
| inner_ring = doc.createElement("LinearRing") | |
| inner_coords = doc.createElement("coordinates") | |
| inner_coord_str = " ".join([f"{point.x()},{point.y()},0" for point in polygon[i]]) | |
| inner_coords.appendChild(doc.createTextNode(inner_coord_str)) | |
| inner_ring.appendChild(inner_coords) | |
| inner_boundary.appendChild(inner_ring) | |
| poly.appendChild(inner_boundary) | |
| return poly | |
| # Fallback para outros tipos ou geometrias não suportadas | |
| return doc.createElement("Polygon") | |
| create_kmz_with_labels( | |
| layer_name="grade_25k", | |
| label_field="mi", | |
| output_kmz_path= "C:/25k.kmz", | |
| line_color="ff4aaf4d", # Verde (ABGR) | |
| line_width=4, | |
| label_color="ff4aaf4d", # Verde (ABGR) | |
| label_scale=2, | |
| min_lod_pixels=200, | |
| char_width_factor=0 | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment