KML (Keyhole Markup Language) is an XML-based format for representing geographic features in applications like Google Earth and Google Maps. This comprehensive guide covers generating, parsing, and manipulating KML files in Java.
Architecture Overview
Java Application → KML Generator → KML Document → DOM/StAX Processing → XML Output → Spatial Data → Points/Lines/Polygons → Style Configuration → Icons/Colors/Labels → File/Stream Output → KMZ Compression
Prerequisites and Setup
Maven Dependencies
<properties>
<jaxb.version>4.0.2</jaxb.version>
<jackson.version>2.15.2</jackson.version>
<jts.version>1.19.0</jts.version>
</properties>
<dependencies>
<!-- XML Processing -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>${jaxb.version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>${jaxb.version}</version>
</dependency>
<!-- JSON for configuration -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- Geometry processing -->
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>${jts.version}</version>
</dependency>
<!-- ZIP compression for KMZ -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.23.0</version>
</dependency>
</dependencies>
KML Namespace Constants
package com.yourapp.kml;
public class KMLConstants {
public static final String KML_NAMESPACE = "http://www.opengis.net/kml/2.2";
public static final String GX_NAMESPACE = "http://www.google.com/kml/ext/2.2";
public static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
public static final String XAL_NAMESPACE = "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0";
public static final String KML_MIME_TYPE = "application/vnd.google-earth.kml+xml";
public static final String KMZ_MIME_TYPE = "application/vnd.google-earth.kmz";
// Common KML style colors (ABGR format)
public static final String RED = "ff0000ff";
public static final String GREEN = "ff00ff00";
public static final String BLUE = "ffff0000";
public static final String YELLOW = "ff00ffff";
public static final String PURPLE = "ffff00ff";
public static final String WHITE = "ffffffff";
public static final String BLACK = "ff000000";
public static final String TRANSPARENT_RED = "800000ff";
public static final String TRANSPARENT_GREEN = "8000ff00";
public static final String TRANSPARENT_BLUE = "80ff0000";
}
Core KML Data Models
1. KML Feature Models
package com.yourapp.kml.model;
import java.util.ArrayList;
import java.util.List;
public class KMLDocument {
private String name;
private String description;
private List<KMLStyle> styles = new ArrayList<>();
private List<KMLPlacemark> placemarks = new ArrayList<>();
private List<KMLFolder> folders = new ArrayList<>();
private KMLView view;
// Constructors
public KMLDocument() {}
public KMLDocument(String name) {
this.name = name;
}
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public List<KMLStyle> getStyles() { return styles; }
public void setStyles(List<KMLStyle> styles) { this.styles = styles; }
public List<KMLPlacemark> getPlacemarks() { return placemarks; }
public void setPlacemarks(List<KMLPlacemark> placemarks) { this.placemarks = placemarks; }
public List<KMLFolder> getFolders() { return folders; }
public void setFolders(List<KMLFolder> folders) { this.folders = folders; }
public KMLView getView() { return view; }
public void setView(KMLView view) { this.view = view; }
// Utility methods
public void addPlacemark(KMLPlacemark placemark) {
this.placemarks.add(placemark);
}
public void addFolder(KMLFolder folder) {
this.folders.add(folder);
}
public void addStyle(KMLStyle style) {
this.styles.add(style);
}
}
public class KMLFolder {
private String id;
private String name;
private String description;
private boolean open = false;
private List<KMLPlacemark> placemarks = new ArrayList<>();
private List<KMLFolder> folders = new ArrayList<>();
// Constructors
public KMLFolder() {}
public KMLFolder(String name) {
this.name = name;
}
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public boolean isOpen() { return open; }
public void setOpen(boolean open) { this.open = open; }
public List<KMLPlacemark> getPlacemarks() { return placemarks; }
public void setPlacemarks(List<KMLPlacemark> placemarks) { this.placemarks = placemarks; }
public List<KMLFolder> getFolders() { return folders; }
public void setFolders(List<KMLFolder> folders) { this.folders = folders; }
// Utility methods
public void addPlacemark(KMLPlacemark placemark) {
this.placemarks.add(placemark);
}
public void addFolder(KMLFolder folder) {
this.folders.add(folder);
}
}
public class KMLPlacemark {
private String id;
private String name;
private String description;
private KMLStyle style;
private String styleUrl;
private KMLGeometry geometry;
private KMLTimePrimitive time;
private KMLView view;
private KMLExtendedData extendedData;
// Constructors
public KMLPlacemark() {}
public KMLPlacemark(String name, KMLGeometry geometry) {
this.name = name;
this.geometry = geometry;
}
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public KMLStyle getStyle() { return style; }
public void setStyle(KMLStyle style) { this.style = style; }
public String getStyleUrl() { return styleUrl; }
public void setStyleUrl(String styleUrl) { this.styleUrl = styleUrl; }
public KMLGeometry getGeometry() { return geometry; }
public void setGeometry(KMLGeometry geometry) { this.geometry = geometry; }
public KMLTimePrimitive getTime() { return time; }
public void setTime(KMLTimePrimitive time) { this.time = time; }
public KMLView getView() { return view; }
public void setView(KMLView view) { this.view = view; }
public KMLExtendedData getExtendedData() { return extendedData; }
public void setExtendedData(KMLExtendedData extendedData) { this.extendedData = extendedData; }
}
2. KML Geometry Models
package com.yourapp.kml.model;
import org.locationtech.jts.geom.*;
import java.util.ArrayList;
import java.util.List;
public abstract class KMLGeometry {
protected String id;
protected KMLStyle style;
public abstract Geometry toJTSGeometry();
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public KMLStyle getStyle() { return style; }
public void setStyle(KMLStyle style) { this.style = style; }
}
public class KMLPoint extends KMLGeometry {
private double longitude;
private double latitude;
private double altitude;
private boolean extrude = false;
private String altitudeMode = "clampToGround";
public KMLPoint(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
public KMLPoint(double latitude, double longitude, double altitude) {
this(latitude, longitude);
this.altitude = altitude;
}
@Override
public Geometry toJTSGeometry() {
GeometryFactory factory = new GeometryFactory();
return factory.createPoint(new Coordinate(longitude, latitude, altitude));
}
// Getters and Setters
public double getLongitude() { return longitude; }
public void setLongitude(double longitude) { this.longitude = longitude; }
public double getLatitude() { return latitude; }
public void setLatitude(double latitude) { this.latitude = latitude; }
public double getAltitude() { return altitude; }
public void setAltitude(double altitude) { this.altitude = altitude; }
public boolean isExtrude() { return extrude; }
public void setExtrude(boolean extrude) { this.extrude = extrude; }
public String getAltitudeMode() { return altitudeMode; }
public void setAltitudeMode(String altitudeMode) { this.altitudeMode = altitudeMode; }
}
public class KMLLineString extends KMLGeometry {
private List<KMLPoint> coordinates = new ArrayList<>();
private boolean extrude = false;
private boolean tessellate = false;
private String altitudeMode = "clampToGround";
public void addCoordinate(double latitude, double longitude) {
coordinates.add(new KMLPoint(latitude, longitude));
}
public void addCoordinate(double latitude, double longitude, double altitude) {
coordinates.add(new KMLPoint(latitude, longitude, altitude));
}
@Override
public Geometry toJTSGeometry() {
GeometryFactory factory = new GeometryFactory();
Coordinate[] coords = coordinates.stream()
.map(p -> new Coordinate(p.getLongitude(), p.getLatitude(), p.getAltitude()))
.toArray(Coordinate[]::new);
return factory.createLineString(coords);
}
// Getters and Setters
public List<KMLPoint> getCoordinates() { return coordinates; }
public void setCoordinates(List<KMLPoint> coordinates) { this.coordinates = coordinates; }
public boolean isExtrude() { return extrude; }
public void setExtrude(boolean extrude) { this.extrude = extrude; }
public boolean isTessellate() { return tessellate; }
public void setTessellate(boolean tessellate) { this.tessellate = tessellate; }
public String getAltitudeMode() { return altitudeMode; }
public void setAltitudeMode(String altitudeMode) { this.altitudeMode = altitudeMode; }
}
public class KMLPolygon extends KMLGeometry {
private KMLLinearRing outerBoundary;
private List<KMLLinearRing> innerBoundaries = new ArrayList<>();
private boolean extrude = false;
private boolean tessellate = false;
private String altitudeMode = "clampToGround";
public KMLPolygon() {}
public KMLPolygon(KMLLinearRing outerBoundary) {
this.outerBoundary = outerBoundary;
}
public void addInnerBoundary(KMLLinearRing innerBoundary) {
this.innerBoundaries.add(innerBoundary);
}
@Override
public Geometry toJTSGeometry() {
GeometryFactory factory = new GeometryFactory();
LinearRing outerRing = (LinearRing) outerBoundary.toJTSGeometry();
LinearRing[] innerRings = innerBoundaries.stream()
.map(ring -> (LinearRing) ring.toJTSGeometry())
.toArray(LinearRing[]::new);
return factory.createPolygon(outerRing, innerRings);
}
// Getters and Setters
public KMLLinearRing getOuterBoundary() { return outerBoundary; }
public void setOuterBoundary(KMLLinearRing outerBoundary) { this.outerBoundary = outerBoundary; }
public List<KMLLinearRing> getInnerBoundaries() { return innerBoundaries; }
public void setInnerBoundaries(List<KMLLinearRing> innerBoundaries) { this.innerBoundaries = innerBoundaries; }
public boolean isExtrude() { return extrude; }
public void setExtrude(boolean extrude) { this.extrude = extrude; }
public boolean isTessellate() { return tessellate; }
public void setTessellate(boolean tessellate) { this.tessellate = tessellate; }
public String getAltitudeMode() { return altitudeMode; }
public void setAltitudeMode(String altitudeMode) { this.altitudeMode = altitudeMode; }
}
public class KMLLinearRing extends KMLGeometry {
private List<KMLPoint> coordinates = new ArrayList<>();
public void addCoordinate(double latitude, double longitude) {
coordinates.add(new KMLPoint(latitude, longitude));
}
public void addCoordinate(double latitude, double longitude, double altitude) {
coordinates.add(new KMLPoint(latitude, longitude, altitude));
}
@Override
public Geometry toJTSGeometry() {
GeometryFactory factory = new GeometryFactory();
Coordinate[] coords = coordinates.stream()
.map(p -> new Coordinate(p.getLongitude(), p.getLatitude(), p.getAltitude()))
.toArray(Coordinate[]::new);
return factory.createLinearRing(coords);
}
// Getters and Setters
public List<KMLPoint> getCoordinates() { return coordinates; }
public void setCoordinates(List<KMLPoint> coordinates) { this.coordinates = coordinates; }
}
public class KMLMultiGeometry extends KMLGeometry {
private List<KMLGeometry> geometries = new ArrayList<>();
public void addGeometry(KMLGeometry geometry) {
geometries.add(geometry);
}
@Override
public Geometry toJTSGeometry() {
GeometryFactory factory = new GeometryFactory();
Geometry[] geomArray = geometries.stream()
.map(KMLGeometry::toJTSGeometry)
.toArray(Geometry[]::new);
return factory.createGeometryCollection(geomArray);
}
// Getters and Setters
public List<KMLGeometry> getGeometries() { return geometries; }
public void setGeometries(List<KMLGeometry> geometries) { this.geometries = geometries; }
}
3. KML Style Models
package com.yourapp.kml.model;
public class KMLStyle {
private String id;
private KMLIconStyle iconStyle;
private KMLLabelStyle labelStyle;
private KMLLineStyle lineStyle;
private KMLPolyStyle polyStyle;
private KMLBalloonStyle balloonStyle;
// Constructors
public KMLStyle() {}
public KMLStyle(String id) {
this.id = id;
}
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public KMLIconStyle getIconStyle() { return iconStyle; }
public void setIconStyle(KMLIconStyle iconStyle) { this.iconStyle = iconStyle; }
public KMLLabelStyle getLabelStyle() { return labelStyle; }
public void setLabelStyle(KMLLabelStyle labelStyle) { this.labelStyle = labelStyle; }
public KMLLineStyle getLineStyle() { return lineStyle; }
public void setLineStyle(KMLLineStyle lineStyle) { this.lineStyle = lineStyle; }
public KMLPolyStyle getPolyStyle() { return polyStyle; }
public void setPolyStyle(KMLPolyStyle polyStyle) { this.polyStyle = polyStyle; }
public KMLBalloonStyle getBalloonStyle() { return balloonStyle; }
public void setBalloonStyle(KMLBalloonStyle balloonStyle) { this.balloonStyle = balloonStyle; }
}
public class KMLIconStyle {
private String color = KMLConstants.WHITE;
private double scale = 1.0;
private double heading = 0.0;
private KMLIcon icon;
// Getters and Setters
public String getColor() { return color; }
public void setColor(String color) { this.color = color; }
public double getScale() { return scale; }
public void setScale(double scale) { this.scale = scale; }
public double getHeading() { return heading; }
public void setHeading(double heading) { this.heading = heading; }
public KMLIcon getIcon() { return icon; }
public void setIcon(KMLIcon icon) { this.icon = icon; }
}
public class KMLIcon {
private String href;
private int refreshInterval = 0;
private String refreshMode = "onChange";
private String viewBoundScale = 1.0;
// Constructors
public KMLIcon() {}
public KMLIcon(String href) {
this.href = href;
}
// Getters and Setters
public String getHref() { return href; }
public void setHref(String href) { this.href = href; }
public int getRefreshInterval() { return refreshInterval; }
public void setRefreshInterval(int refreshInterval) { this.refreshInterval = refreshInterval; }
public String getRefreshMode() { return refreshMode; }
public void setRefreshMode(String refreshMode) { this.refreshMode = refreshMode; }
public String getViewBoundScale() { return viewBoundScale; }
public void setViewBoundScale(String viewBoundScale) { this.viewBoundScale = viewBoundScale; }
}
public class KMLLabelStyle {
private String color = KMLConstants.WHITE;
private double scale = 1.0;
// Getters and Setters
public String getColor() { return color; }
public void setColor(String color) { this.color = color; }
public double getScale() { return scale; }
public void setScale(double scale) { this.scale = scale; }
}
public class KMLLineStyle {
private String color = KMLConstants.WHITE;
private double width = 1.0;
// Getters and Setters
public String getColor() { return color; }
public void setColor(String color) { this.color = color; }
public double getWidth() { return width; }
public void setWidth(double width) { this.width = width; }
}
public class KMLPolyStyle {
private String color = KMLConstants.WHITE;
private boolean fill = true;
private boolean outline = true;
// Getters and Setters
public String getColor() { return color; }
public void setColor(String color) { this.color = color; }
public boolean isFill() { return fill; }
public void setFill(boolean fill) { this.fill = fill; }
public boolean isOutline() { return outline; }
public void setOutline(boolean outline) { this.outline = outline; }
}
public class KMLBalloonStyle {
private String bgColor = KMLConstants.WHITE;
private String textColor = KMLConstants.BLACK;
private String text;
private String displayMode = "default";
// Getters and Setters
public String getBgColor() { return bgColor; }
public void setBgColor(String bgColor) { this.bgColor = bgColor; }
public String getTextColor() { return textColor; }
public void setTextColor(String textColor) { this.textColor = textColor; }
public String getText() { return text; }
public void setText(String text) { this.text = text; }
public String getDisplayMode() { return displayMode; }
public void setDisplayMode(String displayMode) { this.displayMode = displayMode; }
}
4. Supporting Models
package com.yourapp.kml.model;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public class KMLView {
private double longitude;
private double latitude;
private double altitude;
private double heading;
private double tilt;
private double range;
// Constructors
public KMLView() {}
public KMLView(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
this.range = 1000; // Default range
}
// Getters and Setters
public double getLongitude() { return longitude; }
public void setLongitude(double longitude) { this.longitude = longitude; }
public double getLatitude() { return latitude; }
public void setLatitude(double latitude) { this.latitude = latitude; }
public double getAltitude() { return altitude; }
public void setAltitude(double altitude) { this.altitude = altitude; }
public double getHeading() { return heading; }
public void setHeading(double heading) { this.heading = heading; }
public double getTilt() { return tilt; }
public void setTilt(double tilt) { this.tilt = tilt; }
public double getRange() { return range; }
public void setRange(double range) { this.range = range; }
}
public abstract class KMLTimePrimitive {
// Marker class for time primitives
}
public class KMLTimeSpan extends KMLTimePrimitive {
private LocalDateTime begin;
private LocalDateTime end;
// Constructors
public KMLTimeSpan() {}
public KMLTimeSpan(LocalDateTime begin, LocalDateTime end) {
this.begin = begin;
this.end = end;
}
// Getters and Setters
public LocalDateTime getBegin() { return begin; }
public void setBegin(LocalDateTime begin) { this.begin = begin; }
public LocalDateTime getEnd() { return end; }
public void setEnd(LocalDateTime end) { this.end = end; }
public String getBeginString() {
return begin.format(DateTimeFormatter.ISO_DATE_TIME);
}
public String getEndString() {
return end.format(DateTimeFormatter.ISO_DATE_TIME);
}
}
public class KMLTimeStamp extends KMLTimePrimitive {
private LocalDateTime when;
// Constructors
public KMLTimeStamp() {}
public KMLTimeStamp(LocalDateTime when) {
this.when = when;
}
// Getters and Setters
public LocalDateTime getWhen() { return when; }
public void setWhen(LocalDateTime when) { this.when = when; }
public String getWhenString() {
return when.format(DateTimeFormatter.ISO_DATE_TIME);
}
}
public class KMLExtendedData {
private List<KMLData> data = new ArrayList<>();
private List<KMLSchemaData> schemaData = new ArrayList<>();
// Getters and Setters
public List<KMLData> getData() { return data; }
public void setData(List<KMLData> data) { this.data = data; }
public List<KMLSchemaData> getSchemaData() { return schemaData; }
public void setSchemaData(List<KMLSchemaData> schemaData) { this.schemaData = schemaData; }
// Utility methods
public void addData(String name, String value) {
this.data.add(new KMLData(name, value));
}
public void addData(KMLData data) {
this.data.add(data);
}
}
public class KMLData {
private String name;
private String value;
private String displayName;
// Constructors
public KMLData() {}
public KMLData(String name, String value) {
this.name = name;
this.value = value;
}
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
public String getDisplayName() { return displayName; }
public void setDisplayName(String displayName) { this.displayName = displayName; }
}
public class KMLSchemaData {
private String schemaUrl;
private List<KMLSimpleData> simpleData = new ArrayList<>();
// Getters and Setters
public String getSchemaUrl() { return schemaUrl; }
public void setSchemaUrl(String schemaUrl) { this.schemaUrl = schemaUrl; }
public List<KMLSimpleData> getSimpleData() { return simpleData; }
public void setSimpleData(List<KMLSimpleData> simpleData) { this.simpleData = simpleData; }
}
public class KMLSimpleData {
private String name;
private String value;
// Constructors
public KMLSimpleData() {}
public KMLSimpleData(String name, String value) {
this.name = name;
this.value = value;
}
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
Core KML Generator
1. DOM-based KML Generator
package com.yourapp.kml.generator;
import com.yourapp.kml.model.*;
import com.yourapp.kml.KMLConstants;
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.io.OutputStream;
public class DOMKMLGenerator {
private Document document;
public DOMKMLGenerator() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.newDocument();
} catch (Exception e) {
throw new RuntimeException("Failed to create DOM document", e);
}
}
/**
* Generate KML document from KMLDocument model
*/
public String generateKML(KMLDocument kmlDocument) {
try {
// Create root KML element
Element kmlElement = document.createElementNS(KMLConstants.KML_NAMESPACE, "kml");
kmlElement.setAttribute("xmlns", KMLConstants.KML_NAMESPACE);
kmlElement.setAttribute("xmlns:gx", KMLConstants.GX_NAMESPACE);
document.appendChild(kmlElement);
// Create Document element
Element documentElement = createDocumentElement(kmlDocument);
kmlElement.appendChild(documentElement);
// Convert to string
return documentToString();
} catch (Exception e) {
throw new RuntimeException("Failed to generate KML", e);
}
}
/**
* Generate KML and write to output stream
*/
public void generateKML(KMLDocument kmlDocument, OutputStream outputStream) {
try {
String kmlString = generateKML(kmlDocument);
outputStream.write(kmlString.getBytes());
outputStream.flush();
} catch (Exception e) {
throw new RuntimeException("Failed to write KML to output stream", e);
}
}
private Element createDocumentElement(KMLDocument kmlDoc) {
Element docElement = document.createElement("Document");
// Add name and description
if (kmlDoc.getName() != null) {
appendTextElement(docElement, "name", kmlDoc.getName());
}
if (kmlDoc.getDescription() != null) {
appendTextElement(docElement, "description", kmlDoc.getDescription());
}
// Add styles
for (KMLStyle style : kmlDoc.getStyles()) {
docElement.appendChild(createStyleElement(style));
}
// Add folders
for (KMLFolder folder : kmlDoc.getFolders()) {
docElement.appendChild(createFolderElement(folder));
}
// Add placemarks
for (KMLPlacemark placemark : kmlDoc.getPlacemarks()) {
docElement.appendChild(createPlacemarkElement(placemark));
}
// Add view if present
if (kmlDoc.getView() != null) {
docElement.appendChild(createLookAtElement(kmlDoc.getView()));
}
return docElement;
}
private Element createStyleElement(KMLStyle style) {
Element styleElement = document.createElement("Style");
if (style.getId() != null) {
styleElement.setAttribute("id", style.getId());
}
// Add icon style
if (style.getIconStyle() != null) {
styleElement.appendChild(createIconStyleElement(style.getIconStyle()));
}
// Add label style
if (style.getLabelStyle() != null) {
styleElement.appendChild(createLabelStyleElement(style.getLabelStyle()));
}
// Add line style
if (style.getLineStyle() != null) {
styleElement.appendChild(createLineStyleElement(style.getLineStyle()));
}
// Add poly style
if (style.getPolyStyle() != null) {
styleElement.appendChild(createPolyStyleElement(style.getPolyStyle()));
}
// Add balloon style
if (style.getBalloonStyle() != null) {
styleElement.appendChild(createBalloonStyleElement(style.getBalloonStyle()));
}
return styleElement;
}
private Element createIconStyleElement(KMLIconStyle iconStyle) {
Element iconStyleElement = document.createElement("IconStyle");
appendTextElement(iconStyleElement, "color", iconStyle.getColor());
appendTextElement(iconStyleElement, "scale", String.valueOf(iconStyle.getScale()));
appendTextElement(iconStyleElement, "heading", String.valueOf(iconStyle.getHeading()));
if (iconStyle.getIcon() != null) {
Element iconElement = document.createElement("Icon");
appendTextElement(iconElement, "href", iconStyle.getIcon().getHref());
iconStyleElement.appendChild(iconElement);
}
return iconStyleElement;
}
private Element createLabelStyleElement(KMLLabelStyle labelStyle) {
Element labelStyleElement = document.createElement("LabelStyle");
appendTextElement(labelStyleElement, "color", labelStyle.getColor());
appendTextElement(labelStyleElement, "scale", String.valueOf(labelStyle.getScale()));
return labelStyleElement;
}
private Element createLineStyleElement(KMLLineStyle lineStyle) {
Element lineStyleElement = document.createElement("LineStyle");
appendTextElement(lineStyleElement, "color", lineStyle.getColor());
appendTextElement(lineStyleElement, "width", String.valueOf(lineStyle.getWidth()));
return lineStyleElement;
}
private Element createPolyStyleElement(KMLPolyStyle polyStyle) {
Element polyStyleElement = document.createElement("PolyStyle");
appendTextElement(polyStyleElement, "color", polyStyle.getColor());
appendTextElement(polyStyleElement, "fill", polyStyle.isFill() ? "1" : "0");
appendTextElement(polyStyleElement, "outline", polyStyle.isOutline() ? "1" : "0");
return polyStyleElement;
}
private Element createBalloonStyleElement(KMLBalloonStyle balloonStyle) {
Element balloonStyleElement = document.createElement("BalloonStyle");
appendTextElement(balloonStyleElement, "bgColor", balloonStyle.getBgColor());
appendTextElement(balloonStyleElement, "textColor", balloonStyle.getTextColor());
appendTextElement(balloonStyleElement, "displayMode", balloonStyle.getDisplayMode());
if (balloonStyle.getText() != null) {
appendTextElement(balloonStyleElement, "text", balloonStyle.getText());
}
return balloonStyleElement;
}
private Element createFolderElement(KMLFolder folder) {
Element folderElement = document.createElement("Folder");
if (folder.getId() != null) {
folderElement.setAttribute("id", folder.getId());
}
appendTextElement(folderElement, "name", folder.getName());
appendTextElement(folderElement, "description", folder.getDescription());
appendTextElement(folderElement, "open", folder.isOpen() ? "1" : "0");
// Add placemarks
for (KMLPlacemark placemark : folder.getPlacemarks()) {
folderElement.appendChild(createPlacemarkElement(placemark));
}
// Add sub-folders
for (KMLFolder subFolder : folder.getFolders()) {
folderElement.appendChild(createFolderElement(subFolder));
}
return folderElement;
}
private Element createPlacemarkElement(KMLPlacemark placemark) {
Element placemarkElement = document.createElement("Placemark");
if (placemark.getId() != null) {
placemarkElement.setAttribute("id", placemark.getId());
}
appendTextElement(placemarkElement, "name", placemark.getName());
appendTextElement(placemarkElement, "description", placemark.getDescription());
// Add style URL
if (placemark.getStyleUrl() != null) {
appendTextElement(placemarkElement, "styleUrl", placemark.getStyleUrl());
}
// Add inline style
if (placemark.getStyle() != null) {
placemarkElement.appendChild(createStyleElement(placemark.getStyle()));
}
// Add geometry
if (placemark.getGeometry() != null) {
placemarkElement.appendChild(createGeometryElement(placemark.getGeometry()));
}
// Add time primitive
if (placemark.getTime() != null) {
placemarkElement.appendChild(createTimePrimitiveElement(placemark.getTime()));
}
// Add extended data
if (placemark.getExtendedData() != null) {
placemarkElement.appendChild(createExtendedDataElement(placemark.getExtendedData()));
}
// Add view
if (placemark.getView() != null) {
placemarkElement.appendChild(createLookAtElement(placemark.getView()));
}
return placemarkElement;
}
private Element createGeometryElement(KMLGeometry geometry) {
if (geometry instanceof KMLPoint) {
return createPointElement((KMLPoint) geometry);
} else if (geometry instanceof KMLLineString) {
return createLineStringElement((KMLLineString) geometry);
} else if (geometry instanceof KMLPolygon) {
return createPolygonElement((KMLPolygon) geometry);
} else if (geometry instanceof KMLMultiGeometry) {
return createMultiGeometryElement((KMLMultiGeometry) geometry);
} else {
throw new IllegalArgumentException("Unsupported geometry type: " + geometry.getClass().getSimpleName());
}
}
private Element createPointElement(KMLPoint point) {
Element pointElement = document.createElement("Point");
appendTextElement(pointElement, "extrude", point.isExtrude() ? "1" : "0");
appendTextElement(pointElement, "tessellate", "0");
appendTextElement(pointElement, "altitudeMode", point.getAltitudeMode());
// Create coordinates
StringBuilder coords = new StringBuilder();
coords.append(point.getLongitude()).append(",")
.append(point.getLatitude());
if (point.getAltitude() != 0) {
coords.append(",").append(point.getAltitude());
}
appendTextElement(pointElement, "coordinates", coords.toString());
return pointElement;
}
private Element createLineStringElement(KMLLineString lineString) {
Element lineStringElement = document.createElement("LineString");
appendTextElement(lineStringElement, "extrude", lineString.isExtrude() ? "1" : "0");
appendTextElement(lineStringElement, "tessellate", lineString.isTessellate() ? "1" : "0");
appendTextElement(lineStringElement, "altitudeMode", lineString.getAltitudeMode());
// Create coordinates
StringBuilder coords = new StringBuilder();
for (KMLPoint point : lineString.getCoordinates()) {
coords.append(point.getLongitude()).append(",")
.append(point.getLatitude());
if (point.getAltitude() != 0) {
coords.append(",").append(point.getAltitude());
}
coords.append(" ");
}
appendTextElement(lineStringElement, "coordinates", coords.toString().trim());
return lineStringElement;
}
private Element createPolygonElement(KMLPolygon polygon) {
Element polygonElement = document.createElement("Polygon");
appendTextElement(polygonElement, "extrude", polygon.isExtrude() ? "1" : "0");
appendTextElement(polygonElement, "tessellate", polygon.isTessellate() ? "1" : "0");
appendTextElement(polygonElement, "altitudeMode", polygon.getAltitudeMode());
// Outer boundary
if (polygon.getOuterBoundary() != null) {
Element outerBoundaryElement = document.createElement("outerBoundaryIs");
Element linearRingElement = createLinearRingElement(polygon.getOuterBoundary());
outerBoundaryElement.appendChild(linearRingElement);
polygonElement.appendChild(outerBoundaryElement);
}
// Inner boundaries
for (KMLLinearRing innerBoundary : polygon.getInnerBoundaries()) {
Element innerBoundaryElement = document.createElement("innerBoundaryIs");
Element linearRingElement = createLinearRingElement(innerBoundary);
innerBoundaryElement.appendChild(linearRingElement);
polygonElement.appendChild(innerBoundaryElement);
}
return polygonElement;
}
private Element createLinearRingElement(KMLLinearRing linearRing) {
Element linearRingElement = document.createElement("LinearRing");
// Create coordinates
StringBuilder coords = new StringBuilder();
for (KMLPoint point : linearRing.getCoordinates()) {
coords.append(point.getLongitude()).append(",")
.append(point.getLatitude());
if (point.getAltitude() != 0) {
coords.append(",").append(point.getAltitude());
}
coords.append(" ");
}
appendTextElement(linearRingElement, "coordinates", coords.toString().trim());
return linearRingElement;
}
private Element createMultiGeometryElement(KMLMultiGeometry multiGeometry) {
Element multiGeometryElement = document.createElement("MultiGeometry");
for (KMLGeometry geometry : multiGeometry.getGeometries()) {
multiGeometryElement.appendChild(createGeometryElement(geometry));
}
return multiGeometryElement;
}
private Element createTimePrimitiveElement(KMLTimePrimitive timePrimitive) {
if (timePrimitive instanceof KMLTimeSpan) {
return createTimeSpanElement((KMLTimeSpan) timePrimitive);
} else if (timePrimitive instanceof KMLTimeStamp) {
return createTimeStampElement((KMLTimeStamp) timePrimitive);
} else {
throw new IllegalArgumentException("Unsupported time primitive type");
}
}
private Element createTimeSpanElement(KMLTimeSpan timeSpan) {
Element timeSpanElement = document.createElement("TimeSpan");
appendTextElement(timeSpanElement, "begin", timeSpan.getBeginString());
appendTextElement(timeSpanElement, "end", timeSpan.getEndString());
return timeSpanElement;
}
private Element createTimeStampElement(KMLTimeStamp timeStamp) {
Element timeStampElement = document.createElement("TimeStamp");
appendTextElement(timeStampElement, "when", timeStamp.getWhenString());
return timeStampElement;
}
private Element createLookAtElement(KMLView view) {
Element lookAtElement = document.createElement("LookAt");
appendTextElement(lookAtElement, "longitude", String.valueOf(view.getLongitude()));
appendTextElement(lookAtElement, "latitude", String.valueOf(view.getLatitude()));
appendTextElement(lookAtElement, "altitude", String.valueOf(view.getAltitude()));
appendTextElement(lookAtElement, "heading", String.valueOf(view.getHeading()));
appendTextElement(lookAtElement, "tilt", String.valueOf(view.getTilt()));
appendTextElement(lookAtElement, "range", String.valueOf(view.getRange()));
return lookAtElement;
}
private Element createExtendedDataElement(KMLExtendedData extendedData) {
Element extendedDataElement = document.createElement("ExtendedData");
// Add Data elements
for (KMLData data : extendedData.getData()) {
Element dataElement = document.createElement("Data");
dataElement.setAttribute("name", data.getName());
if (data.getDisplayName() != null) {
appendTextElement(dataElement, "displayName", data.getDisplayName());
}
appendTextElement(dataElement, "value", data.getValue());
extendedDataElement.appendChild(dataElement);
}
// Add SchemaData elements
for (KMLSchemaData schemaData : extendedData.getSchemaData()) {
Element schemaDataElement = document.createElement("SchemaData");
schemaDataElement.setAttribute("schemaUrl", schemaData.getSchemaUrl());
for (KMLSimpleData simpleData : schemaData.getSimpleData()) {
Element simpleDataElement = document.createElement("SimpleData");
simpleDataElement.setAttribute("name", simpleData.getName());
simpleDataElement.setTextContent(simpleData.getValue());
schemaDataElement.appendChild(simpleDataElement);
}
extendedDataElement.appendChild(schemaDataElement);
}
return extendedDataElement;
}
private void appendTextElement(Element parent, String tagName, String textContent) {
if (textContent != null) {
Element element = document.createElement(tagName);
element.setTextContent(textContent);
parent.appendChild(element);
}
}
private String documentToString() throws Exception {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(writer));
return writer.toString();
}
}
2. String-based KML Generator (Lightweight)
package com.yourapp.kml.generator;
import com.yourapp.kml.model.*;
import com.yourapp.kml.KMLConstants;
public class SimpleKMLGenerator {
/**
* Generate simple KML string from KMLDocument
*/
public String generateKML(KMLDocument kmlDocument) {
StringBuilder kml = new StringBuilder();
kml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
kml.append("<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n");
kml.append("<Document>\n");
// Document metadata
if (kmlDocument.getName() != null) {
kml.append(" <name>").append(escapeXML(kmlDocument.getName())).append("</name>\n");
}
if (kmlDocument.getDescription() != null) {
kml.append(" <description>").append(escapeXML(kmlDocument.getDescription())).append("</description>\n");
}
// Styles
for (KMLStyle style : kmlDocument.getStyles()) {
kml.append(generateStyle(style, " "));
}
// Folders
for (KMLFolder folder : kmlDocument.getFolders()) {
kml.append(generateFolder(folder, " "));
}
// Placemarks
for (KMLPlacemark placemark : kmlDocument.getPlacemarks()) {
kml.append(generatePlacemark(placemark, " "));
}
kml.append("</Document>\n");
kml.append("</kml>");
return kml.toString();
}
private String generateStyle(KMLStyle style, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<Style id=\"").append(escapeXML(style.getId())).append("\">\n");
if (style.getIconStyle() != null) {
sb.append(generateIconStyle(style.getIconStyle(), indent + " "));
}
if (style.getLabelStyle() != null) {
sb.append(generateLabelStyle(style.getLabelStyle(), indent + " "));
}
if (style.getLineStyle() != null) {
sb.append(generateLineStyle(style.getLineStyle(), indent + " "));
}
if (style.getPolyStyle() != null) {
sb.append(generatePolyStyle(style.getPolyStyle(), indent + " "));
}
sb.append(indent).append("</Style>\n");
return sb.toString();
}
private String generateIconStyle(KMLIconStyle iconStyle, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<IconStyle>\n");
sb.append(indent).append(" <color>").append(iconStyle.getColor()).append("</color>\n");
sb.append(indent).append(" <scale>").append(iconStyle.getScale()).append("</scale>\n");
if (iconStyle.getIcon() != null) {
sb.append(indent).append(" <Icon>\n");
sb.append(indent).append(" <href>").append(escapeXML(iconStyle.getIcon().getHref())).append("</href>\n");
sb.append(indent).append(" </Icon>\n");
}
sb.append(indent).append("</IconStyle>\n");
return sb.toString();
}
private String generateLabelStyle(KMLLabelStyle labelStyle, String indent) {
return indent + "<LabelStyle>\n" +
indent + " <color>" + labelStyle.getColor() + "</color>\n" +
indent + " <scale>" + labelStyle.getScale() + "</scale>\n" +
indent + "</LabelStyle>\n";
}
private String generateLineStyle(KMLLineStyle lineStyle, String indent) {
return indent + "<LineStyle>\n" +
indent + " <color>" + lineStyle.getColor() + "</color>\n" +
indent + " <width>" + lineStyle.getWidth() + "</width>\n" +
indent + "</LineStyle>\n";
}
private String generatePolyStyle(KMLPolyStyle polyStyle, String indent) {
return indent + "<PolyStyle>\n" +
indent + " <color>" + polyStyle.getColor() + "</color>\n" +
indent + " <fill>" + (polyStyle.isFill() ? "1" : "0") + "</fill>\n" +
indent + " <outline>" + (polyStyle.isOutline() ? "1" : "0") + "</outline>\n" +
indent + "</PolyStyle>\n";
}
private String generateFolder(KMLFolder folder, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<Folder>\n");
sb.append(indent).append(" <name>").append(escapeXML(folder.getName())).append("</name>\n");
if (folder.getDescription() != null) {
sb.append(indent).append(" <description>").append(escapeXML(folder.getDescription())).append("</description>\n");
}
sb.append(indent).append(" <open>").append(folder.isOpen() ? "1" : "0").append("</open>\n");
// Add placemarks in folder
for (KMLPlacemark placemark : folder.getPlacemarks()) {
sb.append(generatePlacemark(placemark, indent + " "));
}
// Add sub-folders
for (KMLFolder subFolder : folder.getFolders()) {
sb.append(generateFolder(subFolder, indent + " "));
}
sb.append(indent).append("</Folder>\n");
return sb.toString();
}
private String generatePlacemark(KMLPlacemark placemark, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<Placemark>\n");
if (placemark.getId() != null) {
sb.append(indent).append(" <id>").append(escapeXML(placemark.getId())).append("</id>\n");
}
sb.append(indent).append(" <name>").append(escapeXML(placemark.getName())).append("</name>\n");
if (placemark.getDescription() != null) {
sb.append(indent).append(" <description>").append(escapeXML(placemark.getDescription())).append("</description>\n");
}
if (placemark.getStyleUrl() != null) {
sb.append(indent).append(" <styleUrl>").append(escapeXML(placemark.getStyleUrl())).append("</styleUrl>\n");
}
// Add inline style
if (placemark.getStyle() != null) {
sb.append(generateStyle(placemark.getStyle(), indent + " "));
}
// Add geometry
if (placemark.getGeometry() != null) {
sb.append(generateGeometry(placemark.getGeometry(), indent + " "));
}
// Add extended data
if (placemark.getExtendedData() != null) {
sb.append(generateExtendedData(placemark.getExtendedData(), indent + " "));
}
sb.append(indent).append("</Placemark>\n");
return sb.toString();
}
private String generateGeometry(KMLGeometry geometry, String indent) {
if (geometry instanceof KMLPoint) {
return generatePoint((KMLPoint) geometry, indent);
} else if (geometry instanceof KMLLineString) {
return generateLineString((KMLLineString) geometry, indent);
} else if (geometry instanceof KMLPolygon) {
return generatePolygon((KMLPolygon) geometry, indent);
} else if (geometry instanceof KMLMultiGeometry) {
return generateMultiGeometry((KMLMultiGeometry) geometry, indent);
} else {
throw new IllegalArgumentException("Unsupported geometry type");
}
}
private String generatePoint(KMLPoint point, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<Point>\n");
sb.append(indent).append(" <coordinates>")
.append(point.getLongitude()).append(",")
.append(point.getLatitude());
if (point.getAltitude() != 0) {
sb.append(",").append(point.getAltitude());
}
sb.append("</coordinates>\n");
sb.append(indent).append("</Point>\n");
return sb.toString();
}
private String generateLineString(KMLLineString lineString, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<LineString>\n");
sb.append(indent).append(" <coordinates>");
for (KMLPoint point : lineString.getCoordinates()) {
sb.append(point.getLongitude()).append(",")
.append(point.getLatitude());
if (point.getAltitude() != 0) {
sb.append(",").append(point.getAltitude());
}
sb.append(" ");
}
sb.append("</coordinates>\n");
sb.append(indent).append("</LineString>\n");
return sb.toString();
}
private String generatePolygon(KMLPolygon polygon, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<Polygon>\n");
// Outer boundary
if (polygon.getOuterBoundary() != null) {
sb.append(indent).append(" <outerBoundaryIs>\n");
sb.append(generateLinearRing(polygon.getOuterBoundary(), indent + " "));
sb.append(indent).append(" </outerBoundaryIs>\n");
}
// Inner boundaries
for (KMLLinearRing innerBoundary : polygon.getInnerBoundaries()) {
sb.append(indent).append(" <innerBoundaryIs>\n");
sb.append(generateLinearRing(innerBoundary, indent + " "));
sb.append(indent).append(" </innerBoundaryIs>\n");
}
sb.append(indent).append("</Polygon>\n");
return sb.toString();
}
private String generateLinearRing(KMLLinearRing linearRing, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<LinearRing>\n");
sb.append(indent).append(" <coordinates>");
for (KMLPoint point : linearRing.getCoordinates()) {
sb.append(point.getLongitude()).append(",")
.append(point.getLatitude());
if (point.getAltitude() != 0) {
sb.append(",").append(point.getAltitude());
}
sb.append(" ");
}
sb.append("</coordinates>\n");
sb.append(indent).append("</LinearRing>\n");
return sb.toString();
}
private String generateMultiGeometry(KMLMultiGeometry multiGeometry, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<MultiGeometry>\n");
for (KMLGeometry geometry : multiGeometry.getGeometries()) {
sb.append(generateGeometry(geometry, indent + " "));
}
sb.append(indent).append("</MultiGeometry>\n");
return sb.toString();
}
private String generateExtendedData(KMLExtendedData extendedData, String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent).append("<ExtendedData>\n");
for (KMLData data : extendedData.getData()) {
sb.append(indent).append(" <Data name=\"").append(escapeXML(data.getName())).append("\">\n");
if (data.getDisplayName() != null) {
sb.append(indent).append(" <displayName>").append(escapeXML(data.getDisplayName())).append("</displayName>\n");
}
sb.append(indent).append(" <value>").append(escapeXML(data.getValue())).append("</value>\n");
sb.append(indent).append(" </Data>\n");
}
sb.append(indent).append("</ExtendedData>\n");
return sb.toString();
}
private String escapeXML(String text) {
if (text == null) return "";
return text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'");
}
}
Advanced KML Features
3. KMZ Generator (Compressed KML)
package com.yourapp.kml.generator;
import com.yourapp.kml.model.KMLDocument;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class KMZGenerator {
private final KMLGenerator kmlGenerator;
public KMZGenerator(KMLGenerator kmlGenerator) {
this.kmlGenerator = kmlGenerator;
}
/**
* Generate KMZ file from KML document
*/
public void generateKMZ(KMLDocument kmlDocument, OutputStream outputStream) {
try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {
// Add KML file
ZipEntry kmlEntry = new ZipEntry("doc.kml");
zipOut.putNextEntry(kmlEntry);
String kmlContent = kmlGenerator.generateKML(kmlDocument);
zipOut.write(kmlContent.getBytes());
zipOut.closeEntry();
// Add supporting files (icons, images, etc.)
addSupportingFiles(kmlDocument, zipOut);
zipOut.finish();
} catch (IOException e) {
throw new RuntimeException("Failed to generate KMZ", e);
}
}
/**
* Generate KMZ file and return as byte array
*/
public byte[] generateKMZ(KMLDocument kmlDocument) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
generateKMZ(kmlDocument, outputStream);
return outputStream.toByteArray();
} catch (IOException e) {
throw new RuntimeException("Failed to generate KMZ", e);
}
}
private void addSupportingFiles(KMLDocument kmlDocument, ZipOutputStream zipOut) {
// Extract icon URLs from styles and add them to KMZ
// This is a simplified implementation - in practice, you'd need to
// download and include the actual icon files
}
}
4. KML Style Builder
package com.yourapp.kml.builder;
import com.yourapp.kml.model.*;
import com.yourapp.kml.KMLConstants;
public class KMLStyleBuilder {
/**
* Create a default point style
*/
public static KMLStyle createDefaultPointStyle(String styleId) {
KMLStyle style = new KMLStyle(styleId);
KMLIconStyle iconStyle = new KMLIconStyle();
iconStyle.setColor(KMLConstants.BLUE);
iconStyle.setScale(1.2);
KMLIcon icon = new KMLIcon();
icon.setHref("http://maps.google.com/mapfiles/kml/pal4/icon57.png");
iconStyle.setIcon(icon);
style.setIconStyle(iconStyle);
KMLLabelStyle labelStyle = new KMLLabelStyle();
labelStyle.setColor(KMLConstants.BLACK);
labelStyle.setScale(1.0);
style.setLabelStyle(labelStyle);
return style;
}
/**
* Create a colored point style
*/
public static KMLStyle createColoredPointStyle(String styleId, String color, double scale) {
KMLStyle style = new KMLStyle(styleId);
KMLIconStyle iconStyle = new KMLIconStyle();
iconStyle.setColor(color);
iconStyle.setScale(scale);
KMLIcon icon = new KMLIcon();
icon.setHref("http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png");
iconStyle.setIcon(icon);
style.setIconStyle(iconStyle);
return style;
}
/**
* Create a line style
*/
public static KMLStyle createLineStyle(String styleId, String color, double width) {
KMLStyle style = new KMLStyle(styleId);
KMLLineStyle lineStyle = new KMLLineStyle();
lineStyle.setColor(color);
lineStyle.setWidth(width);
style.setLineStyle(lineStyle);
return style;
}
/**
* Create a polygon style
*/
public static KMLStyle createPolygonStyle(String styleId, String fillColor, String lineColor,
double lineWidth, boolean fill, boolean outline) {
KMLStyle style = new KMLStyle(styleId);
KMLPolyStyle polyStyle = new KMLPolyStyle();
polyStyle.setColor(fillColor);
polyStyle.setFill(fill);
polyStyle.setOutline(outline);
KMLLineStyle lineStyle = new KMLLineStyle();
lineStyle.setColor(lineColor);
lineStyle.setWidth(lineWidth);
style.setPolyStyle(polyStyle);
style.setLineStyle(lineStyle);
return style;
}
/**
* Create a heatmap style for points
*/
public static KMLStyle createHeatmapStyle(String styleId, int intensity) {
KMLStyle style = new KMLStyle(styleId);
KMLIconStyle iconStyle = new KMLIconStyle();
// Create color based on intensity (red for high, blue for low)
String color = calculateHeatmapColor(intensity);
iconStyle.setColor(color);
iconStyle.setScale(0.5 + (intensity * 0.1));
KMLIcon icon = new KMLIcon();
icon.setHref("http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png");
iconStyle.setIcon(icon);
style.setIconStyle(iconStyle);
return style;
}
private static String calculateHeatmapColor(int intensity) {
// Simple color gradient from blue to red
if (intensity < 3) return KMLConstants.BLUE;
if (intensity < 6) return KMLConstants.GREEN;
if (intensity < 9) return KMLConstants.YELLOW;
return KMLConstants.RED;
}
/**
* Create a style for different types of features
*/
public static KMLStyle createCategorizedStyle(String styleId, String category) {
return switch (category.toLowerCase()) {
case "airport" -> createAirportStyle(styleId);
case "restaurant" -> createRestaurantStyle(styleId);
case "hotel" -> createHotelStyle(styleId);
case "hospital" -> createHospitalStyle(styleId);
default -> createDefaultPointStyle(styleId);
};
}
private static KMLStyle createAirportStyle(String styleId) {
KMLStyle style = new KMLStyle(styleId);
KMLIconStyle iconStyle = new KMLIconStyle();
iconStyle.setColor(KMLConstants.BLUE);
iconStyle.setScale(1.5);
KMLIcon icon = new KMLIcon();
icon.setHref("http://maps.google.com/mapfiles/kml/shapes/airports.png");
iconStyle.setIcon(icon);
style.setIconStyle(iconStyle);
return style;
}
private static KMLStyle createRestaurantStyle(String styleId) {
KMLStyle style = new KMLStyle(styleId);
KMLIconStyle iconStyle = new KMLIconStyle();
iconStyle.setColor(KMLConstants.RED);
iconStyle.setScale(1.3);
KMLIcon icon = new KMLIcon();
icon.setHref("http://maps.google.com/mapfiles/kml/shapes/restaurant.png");
iconStyle.setIcon(icon);
style.setIconStyle(iconStyle);
return style;
}
private static KMLStyle createHotelStyle(String styleId) {
KMLStyle style = new KMLStyle(styleId);
KMLIconStyle iconStyle = new KMLIconStyle();
iconStyle.setColor(KMLConstants.GREEN);
iconStyle.setScale(1.3);
KMLIcon icon = new KMLIcon();
icon.setHref("http://maps.google.com/mapfiles/kml/shapes/lodging.png");
iconStyle.setIcon(icon);
style.setIconStyle(iconStyle);
return style;
}
private static KMLStyle createHospitalStyle(String styleId) {
KMLStyle style = new KMLStyle(styleId);
KMLIconStyle iconStyle = new KMLIconStyle();
iconStyle.setColor(KMLConstants.RED);
iconStyle.setScale(1.4);
KMLIcon icon = new KMLIcon();
icon.setHref("http://maps.google.com/mapfiles/kml/shapes/hospitals.png");
iconStyle.setIcon(icon);
style.setIconStyle(iconStyle);
return style;
}
}
Usage Examples
5. KML Generation Service
package com.yourapp.kml.service;
import com.yourapp.kml.builder.KMLStyleBuilder;
import com.yourapp.kml.generator.DOMKMLGenerator;
import com.yourapp.kml.generator.KMZGenerator;
import com.yourapp.kml.generator.SimpleKMLGenerator;
import com.yourapp.kml.model.*;
import org.springframework.stereotype.Service;
import java.io.OutputStream;
import java.util.List;
@Service
public class KMLGenerationService {
private final DOMKMLGenerator domGenerator;
private final SimpleKMLGenerator simpleGenerator;
private final KMZGenerator kmzGenerator;
public KMLGenerationService() {
this.domGenerator = new DOMKMLGenerator();
this.simpleGenerator = new SimpleKMLGenerator();
this.kmzGenerator = new KMZGenerator(simpleGenerator);
}
/**
* Generate KML for a list of points
*/
public String generatePointsKML(List<PointData> points, String documentName) {
KMLDocument kmlDoc = new KMLDocument(documentName);
// Add styles
kmlDoc.addStyle(KMLStyleBuilder.createDefaultPointStyle("default-point"));
// Add points as placemarks
for (PointData point : points) {
KMLPlacemark placemark = new KMLPlacemark();
placemark.setName(point.getName());
placemark.setDescription(point.getDescription());
placemark.setStyleUrl("#default-point");
KMLPoint kmlPoint = new KMLPoint(point.getLatitude(), point.getLongitude());
placemark.setGeometry(kmlPoint);
// Add extended data
KMLExtendedData extendedData = new KMLExtendedData();
extendedData.addData("category", point.getCategory());
extendedData.addData("elevation", String.valueOf(point.getElevation()));
placemark.setExtendedData(extendedData);
kmlDoc.addPlacemark(placemark);
}
return simpleGenerator.generateKML(kmlDoc);
}
/**
* Generate KML for a route with multiple points
*/
public String generateRouteKML(RouteData route) {
KMLDocument kmlDoc = new KMLDocument(route.getName());
// Create line style
KMLStyle routeStyle = KMLStyleBuilder.createLineStyle("route-style",
KMLConstants.BLUE, 4.0);
kmlDoc.addStyle(routeStyle);
// Create route line
KMLLineString routeLine = new KMLLineString();
for (PointData point : route.getPoints()) {
routeLine.addCoordinate(point.getLatitude(), point.getLongitude());
}
KMLPlacemark routePlacemark = new KMLPlacemark();
routePlacemark.setName(route.getName());
routePlacemark.setDescription(route.getDescription());
routePlacemark.setStyleUrl("#route-style");
routePlacemark.setGeometry(routeLine);
kmlDoc.addPlacemark(routePlacemark);
// Add waypoints
KMLStyle waypointStyle = KMLStyleBuilder.createColoredPointStyle(
"waypoint-style", KMLConstants.RED, 1.2);
kmlDoc.addStyle(waypointStyle);
for (int i = 0; i < route.getPoints().size(); i++) {
PointData point = route.getPoints().get(i);
KMLPlacemark waypoint = new KMLPlacemark();
waypoint.setName("Waypoint " + (i + 1));
waypoint.setDescription(point.getName());
waypoint.setStyleUrl("#waypoint-style");
KMLPoint kmlPoint = new KMLPoint(point.getLatitude(), point.getLongitude());
waypoint.setGeometry(kmlPoint);
kmlDoc.addPlacemark(waypoint);
}
return domGenerator.generateKML(kmlDoc);
}
/**
* Generate KML for polygons (e.g., regions, boundaries)
*/
public String generatePolygonsKML(List<PolygonData> polygons, String documentName) {
KMLDocument kmlDoc = new KMLDocument(documentName);
// Create polygon style
KMLStyle polyStyle = KMLStyleBuilder.createPolygonStyle("poly-style",
KMLConstants.TRANSPARENT_RED, KMLConstants.RED, 2.0, true, true);
kmlDoc.addStyle(polyStyle);
for (PolygonData polygon : polygons) {
KMLPlacemark placemark = new KMLPlacemark();
placemark.setName(polygon.getName());
placemark.setDescription(polygon.getDescription());
placemark.setStyleUrl("#poly-style");
KMLPolygon kmlPolygon = new KMLPolygon();
KMLLinearRing outerBoundary = new KMLLinearRing();
for (PointData point : polygon.getBoundary()) {
outerBoundary.addCoordinate(point.getLatitude(), point.getLongitude());
}
kmlPolygon.setOuterBoundary(outerBoundary);
placemark.setGeometry(kmlPolygon);
kmlDoc.addPlacemark(placemark);
}
return simpleGenerator.generateKML(kmlDoc);
}
/**
* Generate KMZ file for download
*/
public void generateKMZFile(KMLDocument kmlDoc, OutputStream outputStream) {
kmzGenerator.generateKMZ(kmlDoc, outputStream);
}
// Data transfer objects
public static class PointData {
private String name;
private String description;
private double latitude;
private double longitude;
private double elevation;
private String category;
// Constructors, getters, and setters
public PointData(String name, double latitude, double longitude) {
this.name = name;
this.latitude = latitude;
this.longitude = longitude;
}
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public double getLatitude() { return latitude; }
public void setLatitude(double latitude) { this.latitude = latitude; }
public double getLongitude() { return longitude; }
public void setLongitude(double longitude) { this.longitude = longitude; }
public double getElevation() { return elevation; }
public void setElevation(double elevation) { this.elevation = elevation; }
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
}
public static class RouteData {
private String name;
private String description;
private List<PointData> points;
// Constructors, getters, and setters
public RouteData(String name, List<PointData> points) {
this.name = name;
this.points = points;
}
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public List<PointData> getPoints() { return points; }
public void setPoints(List<PointData> points) { this.points = points; }
}
public static class PolygonData {
private String name;
private String description;
private List<PointData> boundary;
// Constructors, getters, and setters
public PolygonData(String name, List<PointData> boundary) {
this.name = name;
this.boundary = boundary;
}
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public List<PointData> getBoundary() { return boundary; }
public void setBoundary(List<PointData> boundary) { this.boundary = boundary; }
}
}
This comprehensive KML generation framework provides everything needed to create sophisticated KML files in Java, from simple point data to complex geometries with advanced styling. The system supports both DOM-based and lightweight string-based generation, KMZ compression, and extensive styling options.
Java Observability, Logging Intelligence & AI-Driven Monitoring (APM, Tracing, Logs & Anomaly Detection)
https://macronepal.com/blog/beyond-metrics-observing-serverless-and-traditional-java-applications-with-thundra-apm/
Explains using Thundra APM to observe both serverless and traditional Java applications by combining tracing, metrics, and logs into a unified observability platform for faster debugging and performance insights.
https://macronepal.com/blog/dynatrace-oneagent-in-java-2/
Explains Dynatrace OneAgent for Java, which automatically instruments JVM applications to capture metrics, traces, and logs, enabling full-stack monitoring and root-cause analysis with minimal configuration.
https://macronepal.com/blog/lightstep-java-sdk-distributed-tracing-and-observability-implementation/
Explains Lightstep Java SDK for distributed tracing, helping developers track requests across microservices and identify latency issues using OpenTelemetry-based observability.
https://macronepal.com/blog/honeycomb-io-beeline-for-java-complete-guide-2/
Explains Honeycomb Beeline for Java, which provides high-cardinality observability and deep query capabilities to understand complex system behavior and debug distributed systems efficiently.
https://macronepal.com/blog/lumigo-for-serverless-in-java-complete-distributed-tracing-guide-2/
Explains Lumigo for Java serverless applications, offering automatic distributed tracing, log correlation, and error tracking to simplify debugging in cloud-native environments. (Lumigo Docs)
https://macronepal.com/blog/from-noise-to-signals-implementing-log-anomaly-detection-in-java-applications/
Explains how to detect anomalies in Java logs using behavioral patterns and machine learning techniques to separate meaningful incidents from noisy log data and improve incident response.
https://macronepal.com/blog/ai-powered-log-analysis-in-java-from-reactive-debugging-to-proactive-insights/
Explains AI-driven log analysis for Java applications, shifting from manual debugging to predictive insights that identify issues early and improve system reliability using intelligent log processing.
https://macronepal.com/blog/titliel-java-logging-best-practices/
Explains best practices for Java logging, focusing on structured logs, proper log levels, performance optimization, and ensuring logs are useful for debugging and observability systems.
https://macronepal.com/blog/seeking-a-loguru-for-java-the-quest-for-elegant-and-simple-logging/
Explains the search for simpler, more elegant logging frameworks in Java, comparing modern logging approaches that aim to reduce complexity while improving readability and developer experience.