In kotlin, I want to load a KML file stored as XML and show it with default icon marker.
Here is my actual code:
val content = requireContext().openFileInput("map.kml")
val layer = KmlLayer(map, content, requireContext())
layer.addLayerToMap()
This load the map, and show it but only with small white marker like that:

How can I put default marker ?
I tried to change the marker option with :
for(marker in layer.placemarks) {
marker.markerOptions.icon(null)
}
But it's creating a new marker instead of editing the current one.
So, I tried to don't add the layer, and add marker by myself, but it's not working because of the KmlLayer initialized.
To prevent this, I parsed by myself the file.
Here is my main code:
CoroutineScope(Dispatchers.Main).launch {
try {
val content = requireContext().openFileInput("carte.kmz")
val placemarks = parseKML(content)
for (placemark in placemarks) {
addPlacemarkToMap(placemark, map)
}
} catch (e: Exception) {
Connexion.manageError(e)
}
}
I added this few classes:
data class Placemark(
var name: String = "",
var type: PlacemarkType = PlacemarkType.UNKNOWN,
var coordinates: List<LatLng> = emptyList()
)
enum class PlacemarkType {
POINT, POLYGON, UNKNOWN
}
Then, here is the full parsing code:
private suspend fun parseKML(inputStream: InputStream): List<Placemark> = withContext(Dispatchers.IO) {
val placemarks = mutableListOf<Placemark>()
try {
// Lire tout le contenu d'abord
val bytes = inputStream.readBytes()
// Si c'est un KMZ, extraire le KML
val kmlBytes = if (isKMZ(bytes)) {
extractKMLFromKMZ(bytes)
} else {
bytes
}
val factory = XmlPullParserFactory.newInstance()
val parser = factory.newPullParser()
parser.setInput(kmlBytes.inputStream(), "UTF-8")
var eventType = parser.eventType
var currentPlacemark: Placemark? = null
var currentTag = ""
var inCoordinates = false
while (eventType != XmlPullParser.END_DOCUMENT) {
when (eventType) {
XmlPullParser.START_TAG -> {
currentTag = parser.name
when (currentTag) {
"Placemark" -> currentPlacemark = Placemark()
"Point" -> currentPlacemark?.type = PlacemarkType.POINT
"Polygon" -> currentPlacemark?.type = PlacemarkType.POLYGON
"coordinates" -> inCoordinates = true
}
}
XmlPullParser.TEXT -> {
val text = parser.text?.trim() ?: ""
if (text.isNotEmpty() && currentPlacemark != null) {
when (currentTag) {
"name" -> currentPlacemark.name = text
"coordinates" -> {
if (inCoordinates) {
currentPlacemark.coordinates = parseCoordinates(text)
}
}
}
}
}
XmlPullParser.END_TAG -> {
when (parser.name) {
"Placemark" -> {
currentPlacemark?.let {
if (it.coordinates.isNotEmpty()) {
placemarks.add(it)
}
}
currentPlacemark = null
}
"coordinates" -> inCoordinates = false
}
}
}
eventType = parser.next()
}
} catch (e: Exception) {
println("Error in parseKML: ${e.message}")
e.printStackTrace()
}
placemarks
}
private fun isKMZ(bytes: ByteArray): Boolean {
return try {
// Check for ZIP magic number (PK)
bytes.size >= 2 && bytes[0] == 0x50.toByte() && bytes[1] == 0x4B.toByte()
} catch (e: Exception) {
false
}
}
private fun extractKMLFromKMZ(bytes: ByteArray): ByteArray {
val zipStream = ZipInputStream(bytes.inputStream())
var entry = zipStream.nextEntry
while (entry != null) {
if (entry.name.endsWith(".kml", ignoreCase = true)) {
return zipStream.readBytes()
}
zipStream.closeEntry()
entry = zipStream.nextEntry
}
throw Exception("No KML file found in KMZ")
}
private fun parseCoordinates(coordinatesText: String): List<LatLng> {
val coordinates = mutableListOf<LatLng>()
for (coord in coordinatesText.trim().split(Regex("\\s+"))) {
if (coord.isEmpty()) continue
val parts = coord.split(",")
if (parts.size >= 2) {
try {
coordinates.add(LatLng(parts[1].toDouble(), parts[0].toDouble()))
} catch (e: NumberFormatException) {
println("Error parsing coordinate: $coord")
}
}
}
return coordinates
}