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
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