B4J Code Snippet Load and Save KML file (kmz)

Several have asked how to create a KML file (or KMZ if compressed) starting from a map generated with the GMap library.

A KMZ specification is not simply a compressed KML file, but it can also contain other files, such as marker images if they are customized.

I started from a precious example created by @klaus in this thread. Indeed I take this opportunity and thank him warmly, because in addition to making his project available, it allowed him to know the GoogleMapExt class code and was very instructive.

Starting from this project, I can save the polygons created in a KML with this sub and be opened by Google Earth or absorbed in Google Map or My Map.

You can extend this library to add PolyLine, Markers and other shapes. This then is up to you. I believe that giving the starting point and then allowing others to develop on it is more useful than giving everything ready.

I omitted some function that allow you to find the name and the color of the Polygon, but I am sure you will be able to do it yourself.

Save KML:
SaveKml(file.DirApp,"mypol.kml")

Sub SaveKml(Path As String, NameFile As String)
    Dim PolygonColor As String = "ff0f0f0f" ' gray

    ' KML String
    Dim Kml As String = $"<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
    <Document>
     <name>${NameFile.Replace(".kml","")}</name>
####
[[]]
    </Document>
</kml>"$

        'Style Polygon
        Dim Style As String =""
        For ii= 0 To ListPolygon.Size-1
            ' If possible, the color of the polygons should be different
            Style=$"${Style}
            <Style id="poly-${ii}">
                  <LineStyle>
                    <color>ff000000</color>
                    <width>1.2</width>
                  </LineStyle>
                  <PolyStyle>
                    <color>${PolygonColor}</color>
                    <fill>1</fill>
                    <outline>1</outline>
                  </PolyStyle>
            </Style>"$
        Next

        'Polygon
        Dim PolygonKml As String = $"
    <Folder>
     <name>Congregazioni</name>"$
        Private joPoint, joCurrentDrawPath As JavaObject
        Private joCurrentDrawPath As JavaObject
        joCurrentMapShape = joCurrentMapShape.InitializeNewInstance("com.lynden.gmapsfx.shapes.Polygon", Null)
        Dim NumeroPoligono As Int = 0
        For Each mpg As MapPolygon In ListPolygon
            joCurrentMapShape = mpg
            joCurrentDrawPath = joCurrentDrawPath.InitializeNewInstance("com.lynden.gmapsfx.javascript.object.MVCArray", Null)
            joCurrentDrawPath = joCurrentMapShape.RunMethod("getPath", Null)

        PolygonKml=$"${Poligoni}
      <Placemark>
        <name>${NamePolygon.get(i))}</name>
        <styleUrl>#poly-$1.0{NumeroPoligono}</styleUrl>
        <Polygon>
            <outerBoundaryIs>
            <LinearRing>
            <tessellate>1</tessellate>
            <coordinates>"$
   
            Private i As Int
            Private Size As Int = joCurrentDrawPath.RunMethod("getLength", Null)
 
            For i = 0 To Size - 1
                joPoint = joCurrentDrawPath.RunMethod("getAt", Array As Object (i))
                Private ll As LatLng = joPoint.InitializeNewInstance("com.lynden.gmapsfx.javascript.object.LatLong", Array(joPoint))
                PolygonKml=$"${PolygonKml}
                ${ll.Longitude},${ll.Latitude},0"$
            Next
            PolygonKml=$"${PolygonKml}
            </coordinates>
            </LinearRing>
              </outerBoundaryIs>
      </Polygon>
    </Placemark>"$
            NumeroPoligono=NumeroPoligono+1
        Next
        PolygonKml=$"${PolygonKml}
        </Folder>"$


        Kml=Kml.Replace("[[]]",PolygonKml).Replace("####",Style)
        File.WriteString(Path,NameFile,Kml)

End Sub

P.S: A little note:
The characters #### and [[]] inserted in the KML string, are inserted to be subsequently replaced by the style and coordinates of the polygons. (see row 76)

Each style will be assigned an ID. Each polygon can recall a style by indicating its ID. In this example I numbered them with poly-nn... to facilitate the example.
 
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
For those who want to add markers this is the format.

Obviously I added a staff for the Polygons but empty

Marker:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>DocumentName</name>

<Folder>
'''''' Polygon
</Folder>

<Folder>
<name>Marker</name>
    <Placemark>
        <name>Star-Dust</name>
        <address>12 Street , 12 NY</address>
        <description>Developer</description>
        <MultiGeometry>
            <Point>
            <coordinates>18.00000,37.000000,0</coordinates>
            </Point>
        </MultiGeometry>
    </Placemark>

    <Placemark>
        <name>DOnPerigno</name>
        <address>20 Street , 20 BK</address>
        <description>Developer</description>
        <MultiGeometry>
            <Point>
            <coordinates>18.00000,38.000000,0</coordinates>
            </Point>
        </MultiGeometry>
    </Placemark>

</Folder>
</Document>
</kml>
 

Star-Dust

Expert
Licensed User
Longtime User
To the code already posted, I add a method to load the KML files on the map. In case you have a KMZ, you need to unzip it and upload doc.kml.

P.S. For convenience I don't read the polygon style labels, so I assign the colors around a list of colors.
N.B. This method can also be used for Markers .... but you will know how to modify the source to obtain this too

B4X:
Private Sub LoadMapKML(Path As String,Namefile As String)
    Dim WheelColor As Int = 0
    Dim Kml As String = File.ReadString(Path,Namefile)

    Dim Placemarks As Matcher = Regex.Matcher("<Placemark>[\s\S]*?<\/Placemark>",Kml)

    Do While Placemarks.Find
        Dim Placemark As String = Placemarks.Match
        If Placemark.IndexOf("<Polygon>")>-1 Then ' ONLY FOR POLYGON
            Dim ListaCoordinate As List
            ListaCoordinate.Initialize
      
            Dim Name As Matcher =Regex.Matcher("<name>[\s\S]*?<\/name>",Placemark)
            Dim Polygons As Matcher = Regex.Matcher("<Polygon>[\s\S]*?<\/Polygon>",Placemark)
            Do While Polygons.Find
                ' singolo poligono
                Dim Polygon As String = Polygons.Match
                Dim Coordinates As Matcher = Regex.Matcher("<coordinates>[\s\S]*?<\/coordinates>",Polygon)
                Do While Coordinates.Find
                    Dim Coordinate As String = Coordinates.Match
                    Coordinate=Coordinate.Replace("<coordinates>","").Replace("</coordinates>","").Trim
                    Coordinate=Coordinate.Replace(Chr(10)," ").Replace(Chr(13),"").Replace("   "," ").Replace("  "," ").Replace("  "," ")
                    Dim ll() As String = Regex.Split(" ",Coordinate)
                    Dim ArrayCoordinate() As String = Regex.Split(" ",Coordinate)
                    For Each RowCoo As String In ArrayCoordinate
                        Dim SingleCoord() As String = Regex.Split(",",RowCoo)
                        If SingleCoord.Length>1 Then
                            Dim LatLon As LatLng
                            LatLon.Initialize(SingleCoord(1),SingleCoord(0))
                            Log(LatLon.Latitude & "    ,   " & LatLon.Longitude)
                            ListaCoordinate.Add(LatLon)
                       End IF
                    Next
                Loop
            Loop
            'Add Polygon
            If Name.Find Then
                Log(Name) ' Name of Polygon
            End If
            Private mpg As MapPolygon = GMap.AddPolygon(ListaCoordinate, LineWidthPolygon, LineColorPolygon, SelectColor(WheelColor), OpacityPolygon)
            WheelColor = WheelColor  + 1
        End If
    Loop
End Sub

Sub SelectColor(ColorProgress As Int) As Paint
    Dim C As Paint
    Select ColorProgress mod 16
        Case 0
            C=fx.Colors.Black
        Case 1
            c=fx.Colors.Blue
        Case 2
            c=fx.Colors.Yellow
        Case 3
            c=fx.Colors.DarkGray
        Case 4
            c=fx.Colors.Green
        Case 5
            c=fx.Colors.Magenta
        Case 6
            c=fx.Colors.RGB(120,40,60)
        Case 7
            c=fx.Colors.Gray
        Case 8
            c=fx.Colors.Red
        Case 9
            c=fx.Colors.Cyan
        Case 10
            c=fx.Colors.RGB(100,100,0)
        Case 11
            c=fx.Colors.RGB(250,50,100)
        Case 12
            c=fx.Colors.RGB(100,10,180)
        Case 13
            c=fx.Colors.RGB(150,100,50)
        Case 14
            c=fx.Colors.RGB(50,150,100)
        Case Else
            c=fx.Colors.RGB(180,180,180)
    End Select

    Return C
End Sub
 
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
See @klaus example post # 1
 

javiers

Active Member
Licensed User
Longtime User
Hi, I use the code to upload a KML file, but I am getting an error. I honestly do not understand the code to know what is happening.

I put the error and the kml file that I want to load. Is this possible to do? It is a file that is generated with the NOAA HYSPLIT utility.
https://www.ready.noaa.gov/hypub-bin/hyresults.pl?jobidno=27623



 

Attachments

  • HYSPLIT_27623.zip
    16.9 KB · Views: 311

Star-Dust

Expert
Licensed User
Longtime User
He gives me no error. What code are you using? In which line does it generate the error?
Because the error indicates that you are going beyond the maximum value of the index.
 

javiers

Active Member
Licensed User
Longtime User
Thanks for your quick response.

I make this call to the LoadMapKml function


B4X:
    LoadMapKML(File.DirAssets,"HYSPLIT_27623.kml")


B4X:
Private Sub LoadMapKML(Path As String,Namefile As String)
    Dim WheelColor As Int = 0
    Dim Kml As String = File.ReadString(Path,Namefile)

    Dim Placemarks As Matcher = Regex.Matcher("<Placemark>[\s\S]*?<\/Placemark>",Kml)
 
    Do While Placemarks.Find
        Dim Placemark As String = Placemarks.Match
        If Placemark.IndexOf("<Polygon>")>-1 Then ' ONLY FOR POLYGON
            Dim ListaCoordinate As List
            ListaCoordinate.Initialize
        
            Dim Name As Matcher =Regex.Matcher("<name>[\s\S]*?<\/name>",Placemark)
            Dim Polygons As Matcher = Regex.Matcher("<Polygon>[\s\S]*?<\/Polygon>",Placemark)
            Do While Polygons.Find
                ' singolo poligono
                Dim Polygon As String = Polygons.Match
                Dim Coordinates As Matcher = Regex.Matcher("<coordinates>[\s\S]*?<\/coordinates>",Polygon)
                Do While Coordinates.Find
                    Dim Coordinate As String = Coordinates.Match
                    Coordinate=Coordinate.Replace(Chr(10),"").Replace(Chr(13),"").Replace("<coordinates>","").Replace("</coordinates>","").Trim
                    Dim ArrayCoordinate() As String = Regex.Split(" ",Coordinate)
                    For Each RowCoo As String In ArrayCoordinate
                        Dim SingleCoord() As String = Regex.Split(",",RowCoo)
                        Dim LatLon As LatLng
                        LatLon.Initialize(SingleCoord(1),SingleCoord(0))
                        Log(LatLon.Latitude & "    ,   " & LatLon.Longitude)
                        ListaCoordinate.Add(LatLon)
                    Next
                Loop
            Loop
            'Add Polygon
            If Name .Find Then
                Log(Name) ' Name of Polygon
            End If
            Private mpg As MapPolygon = GMap.AddPolygon(ListaCoordinate, LineWidthPolygon, LineColorPolygon, SelectColor(WheelColor), OpacityPolygon)
            WheelColor = WheelColor  + 1
        End If
    Loop
End Sub

Sub SelectColor(ColorProgress As Int) As Paint
    Dim C As Paint
    Select ColorProgress Mod 16
        Case 0
            C=fx.Colors.Black
        Case 1
            c=fx.Colors.Blue
        Case 2
            c=fx.Colors.Yellow
        Case 3
            c=fx.Colors.DarkGray
        Case 4
            c=fx.Colors.Green
        Case 5
            c=fx.Colors.Magenta
        Case 6
            c=fx.Colors.RGB(120,40,60)
        Case 7
            c=fx.Colors.Gray
        Case 8
            c=fx.Colors.Red
        Case 9
            c=fx.Colors.Cyan
        Case 10
            c=fx.Colors.RGB(100,100,0)
        Case 11
            c=fx.Colors.RGB(250,50,100)
        Case 12
            c=fx.Colors.RGB(100,10,180)
        Case 13
            c=fx.Colors.RGB(150,100,50)
        Case 14
            c=fx.Colors.RGB(50,150,100)
        Case Else
            c=fx.Colors.RGB(180,180,180)
    End Select

    Return C
End Sub
 

Star-Dust

Expert
Licensed User
Longtime User
On which line do you get the error?
 

javiers

Active Member
Licensed User
Longtime User
The error is on the line
B4X:
LatLon.Initialize (SingleCoord (1), SingleCoord (0))
 

Star-Dust

Expert
Licensed User
Longtime User
Change like that
B4X:
Private Sub LoadMapKML(Path As String,Namefile As String)
    Dim WheelColor As Int = 0
    Dim Kml As String = File.ReadString(Path,Namefile)

    Dim Placemarks As Matcher = Regex.Matcher("<Placemark>[\s\S]*?<\/Placemark>",Kml)

    Do While Placemarks.Find
        Dim Placemark As String = Placemarks.Match
        If Placemark.IndexOf("<Polygon>")>-1 Then ' ONLY FOR POLYGON
            Dim ListaCoordinate As List
            ListaCoordinate.Initialize
      
            Dim Name As Matcher =Regex.Matcher("<name>[\s\S]*?<\/name>",Placemark)
            Dim Polygons As Matcher = Regex.Matcher("<Polygon>[\s\S]*?<\/Polygon>",Placemark)
            Do While Polygons.Find
                ' singolo poligono
                Dim Polygon As String = Polygons.Match
                Dim Coordinates As Matcher = Regex.Matcher("<coordinates>[\s\S]*?<\/coordinates>",Polygon)
                Do While Coordinates.Find
                    Dim Coordinate As String = Coordinates.Match
                    Coordinate=Coordinate.Replace("<coordinates>","").Replace("</coordinates>","").Trim
                    Coordinate=Coordinate.Replace(Chr(10)," ").Replace(Chr(13),"").Replace("   "," ").Replace("  "," ").Replace("  "," ")
                    Dim ll() As String = Regex.Split(" ",Coordinate)
                    Dim ArrayCoordinate() As String = Regex.Split(" ",Coordinate)
                    For Each RowCoo As String In ArrayCoordinate
                        Dim SingleCoord() As String = Regex.Split(",",RowCoo)
                        If SingleCoord.Length>1 Then
                            Dim LatLon As LatLng
                            LatLon.Initialize(SingleCoord(1),SingleCoord(0))
                            Log(LatLon.Latitude & "    ,   " & LatLon.Longitude)
                            ListaCoordinate.Add(LatLon)
                       End IF
                    Next
                Loop
            Loop
            'Add Polygon
            If Name .Find Then
                Log(Name) ' Name of Polygon
            End If
            Private mpg As MapPolygon = GMap.AddPolygon(ListaCoordinate, LineWidthPolygon, LineColorPolygon, SelectColor(WheelColor), OpacityPolygon)
            WheelColor = WheelColor  + 1
        End If
    Loop
End Sub
 

javiers

Active Member
Licensed User
Longtime User
Thanks! He draws it well.
But I am not able to set the fill color, to draw the graph with the original values.
 

Attachments

  • kmz.jpg
    41.3 KB · Views: 307

Star-Dust

Expert
Licensed User
Longtime User
Thanks! He draws it well.
But I am not able to set the fill color, to draw the graph with the original values.
The colors are specified within the polygon, with a content log you can see the tag and isolate it with a Regex. It's not complex, I'm very busy at the moment and can't write an example, but you can try.
 

Star-Dust

Expert
Licensed User
Longtime User
For the color you have to find the TAG Styles within the KML.
These are accompanied by an ID indicating the name/ID of the polygon. As you can see in the example inside, the color and other characteristics are brambled

XML:
 <Style id="conc1">
    <LineStyle>
      <color>C8000000</color>
    </LineStyle>
    <PolyStyle>
      <color>C8FFFFFF</color>
      <fill>1</fill>
      <outline>1</outline>
    </PolyStyle>
  </Style>


B4X:
Dim Styles As Matcher = Regex.Matcher("<Style>[\s\S]*?<\/Style>",Kml)
 
Last edited:
Cookies are required to use this site. You must accept them to continue using the site. Learn more…