raw Software

A KMZ file is a ZIP archive containing a KML document and any resources referenced by it. Google Earth expects the main document to be named doc.kml at the archive root. Images can live in subdirectories as long as their KML references use the same relative paths.

The following PHP function packages one generated KML document together with every PNG icon from a local directory:

<?php
function createKmz(string $outputPath, string $iconDirectory): void
{
    $kml = <<<KML
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>Example map</name>
    <Style id="marker">
      <IconStyle>
        <Icon>
          <href>files/marker.png</href>
        </Icon>
      </IconStyle>
    </Style>
    <Placemark>
      <name>Example location</name>
      <description>Created dynamically with PHP.</description>
      <styleUrl>#marker</styleUrl>
      <Point>
        <coordinates>37.545734,14.159431,0</coordinates>
      </Point>
    </Placemark>
  </Document>
</kml>
KML;

    $zip = new ZipArchive();
    $result = $zip->open(
        $outputPath,
        ZipArchive::CREATE | ZipArchive::OVERWRITE
    );

    if ($result !== true) {
        throw new RuntimeException(
            "Unable to create KMZ archive (ZipArchive error {$result})"
        );
    }

    try {
        if (!$zip->addFromString('doc.kml', $kml)) {
            throw new RuntimeException('Unable to add doc.kml');
        }

        foreach (glob($iconDirectory . '/*.png') ?: [] as $file) {
            $archivePath = 'files/' . basename($file);

            if (!$zip->addFile($file, $archivePath)) {
                throw new RuntimeException("Unable to add {$file}");
            }
        }
    } finally {
        if (!$zip->close()) {
            throw new RuntimeException('Unable to finalize KMZ archive');
        }
    }
}

createKmz(__DIR__ . '/GoogleEarth.kmz', __DIR__ . '/icons');

Archive Layout

With an icons/marker.png source file, the resulting archive contains:

GoogleEarth.kmz
├── doc.kml
└── files/
    └── marker.png

The KML reference files/marker.png is relative to doc.kml, so it resolves inside the archive. ZIP entries always use forward slashes, even when the PHP code runs on Windows.

Important Details

For a download endpoint, write the archive to a temporary file, send it with the media type application/vnd.google-earth.kmz, and delete the temporary file after the response has been transmitted.