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.