pythonazurekmlgoogle-earthkmz

My kmz generator app not showing custom icon on remote user's machine


I wrote a .kmz file generator using a google places API. The program works great on my local machine but once I launched it on Azure, user's are not receiving the custom icon when opened on google earth. It just displays the white box with red x in the center as the icon. I have verified that the icon is downloading inside the .kmz file along with the kml file by having the remote user change .kmz to .zip and unzipping the file to see the contents. There they find the kml file and the custom icon png file so It must be something with where I'm placing it or how google earth references icons in the kmz...? I'm at a lost I've tried everything. Here's the main part of my app:

import requests
from fastkml import kml
from fastkml import styles
from fastkml.geometry import Point
from fastkml.styles import IconStyle
import xml.etree.ElementTree as ET
from pykml.factory import KML_ElementMaker as KML
from pykml.parser import fromstring as kml_fromstring
import os
import zipfile
import tempfile
import shutil


# Get the path of the current script
script_dir = os.path.dirname(os.path.abspath(__file__))

# Construct the path to the icon file
icon_filename = 'Bank Icon.png'
icon_path = os.path.join(script_dir, icon_filename)

# Google Maps API key
api_key = "My_API_key"

def search_institution_locations(institution, location):
    search_url = "https://maps.googleapis.com/maps/api/place/textsearch/xml"
    query = f"{institution} in {location}"
    params = {
        "query": query,
        "key": api_key,
    }
    try:
        response = requests.get(search_url, params=params)
        response.raise_for_status()
    except Exception as e:
        print(f"An error occurred while searching for {institution} in {location}: {e}")
        return None

    # Print the response text for debugging purposes
    print(response.text)

    return response.text

def generate_kml(institution, city=None, state=None, icon_path=None):
    print('kml generator initiated')

    # Update the icon_path if it's not provided
    if icon_path is None:
        script_dir = os.path.dirname(os.path.abspath(__file__))
        icon_filename = 'Bank Icon.png'
        icon_path = os.path.join(script_dir, icon_filename)

    # Determine the location based on the search scope
    location = f"{city}, {state}"

    # Search for institution locations within the location
    search_results = search_institution_locations(institution, location)

    # Parse the XML search results
    root = ET.fromstring(search_results)

    # Create a KML document
    doc = kml.Document()

    # Create the IconStyle with the custom icon
    icon_style = styles.IconStyle(icon_href=icon_path, scale=2.0)

    # Create the LabelStyle with a scale of 0.0 to hide the text
    label_style = styles.LabelStyle(scale=0.0)

    # Combine the IconStyle and LabelStyle into a Style
    style = styles.Style(id='icon', styles=[icon_style, label_style])
    doc.append_style(style)

    # Add Placemarks for each search result
    for result in root.findall("result"):
        name = result.find("name").text
        address = result.find("formatted_address").text
        lat = float(result.find("geometry/location/lat").text)
        lng = float(result.find("geometry/location/lng").text)

        placemark = kml.Placemark(
            name=name,
            description=address,
            styleUrl="#icon"  # Use the custom icon style
        )
        placemark.geometry = Point((lng, lat))
        doc.append(placemark)

    # Save KML file
    with tempfile.NamedTemporaryFile(suffix='.kml', delete=False) as temp:
        print(doc.to_string(prettyprint=True))
        kml_file_path = temp.name
        with open(kml_file_path, "w", encoding="utf-8") as f:
            f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
            f.write(doc.to_string(prettyprint=True))

    # Create KMZ file
    kmz_file_name = f"{institution.replace(' ', '_')}_locations.kmz"
    with tempfile.NamedTemporaryFile(suffix='.kmz', delete=False) as temp:
        kmz_file_path = temp.name

    with zipfile.ZipFile(kmz_file_path, 'w', zipfile.ZIP_DEFLATED) as zf:
        zf.write(kml_file_path, arcname=os.path.splitext(kmz_file_name)[0] + '.kml')  # Modified line
        zf.write(icon_path, arcname=os.path.basename(icon_path))


    # Remove the temporary KML file
    os.remove(kml_file_path)

    return kmz_file_path

Solution

  • Just wanted to update this since I ended up figuring out the answer on my own. The issue was that the Bank Icon.png needed to be in a directory named "files" and I had to update the kml_generator to reference the new location. Here is the updated code:

    import requests
    from fastkml import kml
    from fastkml import styles
    from fastkml.geometry import Point
    from fastkml.styles import IconStyle
    import xml.etree.ElementTree as ET
    from pykml.factory import KML_ElementMaker as KML
    from pykml.parser import fromstring as kml_fromstring
    import os
    import zipfile
    import tempfile
    import pathlib
    import shutil
    
    
    # Get the path of the current script
    script_dir = os.path.dirname(os.path.abspath(__file__))
    
    # Construct the path to the icon file
    icon_filename = 'Bank Icon.png'
    icon_path = os.path.join('files', icon_filename)
    
    # Create the 'files' directory if it doesn't exist
    files_dir = os.path.join(script_dir, 'files')
    pathlib.Path(files_dir).mkdir(parents=True, exist_ok=True)
    
    # Copy the 'Bank Icon.png' to the 'files' directory if it doesn't exist
    icon_src_path = os.path.join(script_dir, icon_filename)
    icon_dest_path = os.path.join(files_dir, icon_filename)
    if not os.path.exists(icon_dest_path):
        shutil.copy(icon_src_path, icon_dest_path)
    
    # Google Maps API key
    api_key = "My_Google_Place_API_Key"
    
    def search_institution_locations(institution, location):
        search_url = "https://maps.googleapis.com/maps/api/place/textsearch/xml"
        query = f"{institution} in {location}"
        params = {
            "query": query,
            "key": api_key,
        }
        try:
            response = requests.get(search_url, params=params)
            response.raise_for_status()
        except Exception as e:
            print(f"An error occurred while searching for {institution} in {location}: {e}")
            return None
    
        # Print the response text for debugging purposes
        print(response.text)
    
        return response.text
    
    def generate_kml(institution, city=None, state=None, icon_path=None):
        print('kml generator initiated')
    
        # Update the icon_path if it's not provided
        if icon_path is None:
            icon_filename = 'Bank Icon.png'
            icon_path = os.path.join('files', icon_filename)
    
    
        # Determine the location based on the search scope
        location = f"{city}, {state}"
    
        # Search for institution locations within the location
        search_results = search_institution_locations(institution, location)
    
        # Parse the XML search results
        root = ET.fromstring(search_results)
    
        # Create a KML document
        doc = kml.Document()
    
        # Create the IconStyle with the custom icon
        icon_style = styles.IconStyle(icon_href=icon_path, scale=2.0)
    
    
        # Create the LabelStyle with a scale of 0.0 to hide the text
        label_style = styles.LabelStyle(scale=0.0)
    
        # Combine the IconStyle and LabelStyle into a Style
        style = styles.Style(id='icon', styles=[icon_style, label_style])
        doc.append_style(style)
    
        # Add Placemarks for each search result
        for result in root.findall("result"):
            name = result.find("name").text
            address = result.find("formatted_address").text
            lat = float(result.find("geometry/location/lat").text)
            lng = float(result.find("geometry/location/lng").text)
    
            placemark = kml.Placemark(
                name=name,
                description=address,
                styleUrl="#icon"  # Use the custom icon style
            )
            placemark.geometry = Point((lng, lat))
            doc.append(placemark)
    
        # Save KML file
        with tempfile.NamedTemporaryFile(suffix='.kml', delete=False) as temp:
            print(doc.to_string(prettyprint=True))
            kml_file_path = temp.name
            with open(kml_file_path, "w", encoding="utf-8") as f:
                f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
                f.write(doc.to_string(prettyprint=True))
    
        # Create KMZ file
        kmz_file_name = f"{institution.replace(' ', '_')}_locations.kmz"
        with tempfile.NamedTemporaryFile(suffix='.kmz', delete=False) as temp:
            kmz_file_path = temp.name
    
        with zipfile.ZipFile(kmz_file_path, 'w', zipfile.ZIP_DEFLATED) as zf:
            zf.write(kml_file_path, arcname=os.path.splitext(kmz_file_name)[0] + '.kml')
            zf.write(os.path.join(script_dir, icon_filename), arcname=os.path.join('files', icon_filename))
    
        # Remove the temporary KML file
        os.remove(kml_file_path)
    
        return kmz_file_path