<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/env python
# -*- coding: utf-8 -*-
###############################################################################
# $Id$
#
# Project:  GDAL/OGR Test Suite
# Purpose:  LIBKML Driver testing.
# Author:   Even Rouault &lt;even dot rouault at mines dash paris dot org&gt;
# 
###############################################################################
# Copyright (c) 2010-2014, Even Rouault &lt;even dot rouault at mines-paris dot org&gt;
# 
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
###############################################################################

import os
import sys
import string

sys.path.append( '../pymod' )

import gdaltest
import ogrtest
from osgeo import ogr
from osgeo import osr
from osgeo import gdal

###############################################################################
# Test basic open operation for KML datastore.
#
def ogr_libkml_datastore():
    
    ogrtest.kml_ds = None
    ogrtest.have_read_libkml = 0
    ogrtest.kml_drv = None
    ogrtest.libkml_drv = None

    try:
        ogrtest.kml_drv = ogr.GetDriverByName('LIBKML')
    except:
        pass

    if ogrtest.kml_drv is None:
        return 'skip'

    try:
        ogrtest.kml_drv = ogr.GetDriverByName('KML')
    except:
        pass

    # Unregister KML driver if present as its behaviour is not identical
    # to new LIBKML driver
    if ogrtest.kml_drv is not None:
        print('Unregister KML driver')
        ogrtest.kml_drv.Deregister()

    try:
        ogrtest.kml_ds = ogr.Open( 'data/samples.kml' );
    except:
        pass

    if ogrtest.kml_ds is not None:
        ogrtest.have_read_libkml = 1

    if not ogrtest.have_read_libkml:
        return 'skip'

    if ogrtest.kml_ds.GetLayerCount() != 10:
        gdaltest.post_reason( 'wrong number of layers' )
        print(ogrtest.kml_ds.GetLayerCount())
        return 'fail'

    return 'success'

###############################################################################
# Test reading attributes for first layer (point).
#
def ogr_libkml_attributes_1():
    
    if not ogrtest.have_read_libkml:
        return 'skip'

    if ogrtest.kml_ds is None:
        gdaltest.post_reason( 'libkml_ds is none' )
        return 'faii'

    lyr = ogrtest.kml_ds.GetLayerByName('Placemarks')
    feat = lyr.GetNextFeature()

    if feat.GetField('Name') != 'Simple placemark':
        gdaltest.post_reason( 'Wrong name field value' )
        return 'fail'

    if feat.GetField('description')[:23] != 'Attached to the ground.':
        gdaltest.post_reason( 'Wrong description field value' )
        print('got: ', feat.GetField('description')[:23])
        return 'fail'

    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' )
        return 'fail'

    if feat.GetField('Name') != 'Floating placemark':
        gdaltest.post_reason( 'Wrong name field value' )
        return 'fail'

    if feat.GetField('description')[:25] != 'Floats a defined distance':
        gdaltest.post_reason( 'Wrong description field value' )
        print('got: ', feat.GetField('description')[:25])
        return 'fail'
    
    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' )
        return 'fail'

    if feat.GetField('Name') != 'Extruded placemark':
        gdaltest.post_reason( 'Wrong name field value' )
        return 'fail'

    if feat.GetField('description') != 'Tethered to the ground by a customizable\n          \"tail\"':
        gdaltest.post_reason( 'Wrong description field value' )
        print('got: ', feat.GetField('description'))
        return 'fail'

    return 'success'

###############################################################################
# Test reading attributes for another layer (point).
#
def ogr_libkml_attributes_2():
    
    if not ogrtest.have_read_libkml:
        return 'skip'

    if ogrtest.kml_ds is None:
        gdaltest.post_reason( 'libkml_ds is none' )
        return 'faii'

    lyr = ogrtest.kml_ds.GetLayerByName('Highlighted Icon')
    feat = lyr.GetNextFeature()

    if feat.GetField('Name') != 'Roll over this icon':
        gdaltest.post_reason( 'Wrong name field value' )
        return 'fail'

    if feat.GetField('description') != None:
        gdaltest.post_reason( 'Wrong description field value' )
        print("'%s'" % feat.GetField('description'))
        return 'fail'

    feat = lyr.GetNextFeature()
    if feat is not None:
        gdaltest.post_reason( 'unexpected feature found.' )
        return 'fail'

    return 'success'

###############################################################################
# Test reading attributes for another layer (linestring).
#
def ogr_libkml_attributes_3():
    
    if not ogrtest.have_read_libkml:
        return 'skip'

    if ogrtest.kml_ds is None:
        gdaltest.post_reason( 'libkml_ds is none' )
        return 'faii'

    lyr = ogrtest.kml_ds.GetLayerByName('Paths')
    feat = lyr.GetNextFeature()

    if feat.GetField('Name') != 'Tessellated':
        gdaltest.post_reason( 'Wrong name field value' )
        return 'fail'

    if feat.GetField('description') != 'If the &lt;tessellate&gt; tag has a value of 1, the line will contour to the underlying terrain':
        gdaltest.post_reason( 'Wrong description field value' )
        return 'fail'

    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' )
        return 'fail'
    
    if feat.GetField('Name') != 'Untessellated':
        gdaltest.post_reason( 'Wrong name field value' )
        return 'fail'

    if feat.GetField('description') != 'If the &lt;tessellate&gt; tag has a value of 0, the line follow a simple straight-line path from point to point':
        gdaltest.post_reason( 'Wrong description field value' )
        return 'fail'
    
    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' )
        return 'fail'
 
    return 'success'

###############################################################################
# Test reading attributes for another layer (polygon).
#
def ogr_libkml_attributes_4():
    
    if not ogrtest.have_read_libkml:
        return 'skip'

    if ogrtest.kml_ds is None:
        gdaltest.post_reason( 'libkml_ds is none' )
        return 'faii'

    lyr = ogrtest.kml_ds.GetLayerByName('Google Campus')
    feat = lyr.GetNextFeature()

    i = 40
    while feat is not None:
        name = 'Building %d' % i
        if feat.GetField('Name') != name:
            gdaltest.post_reason( 'Wrong name field value' )
            print('Got: "%s"' % feat.GetField('name'))
            return 'fail'

        if feat.GetField('description') != None:
            gdaltest.post_reason( 'Wrong description field value' )
            return 'fail'

        i = i + 1
        feat = lyr.GetNextFeature()

    return 'success'
 
###############################################################################
# Test reading of KML point geometry
#
def ogr_libkml_point_read():
    
    if not ogrtest.have_read_libkml:
        return 'skip'

    if ogrtest.kml_ds is None:
        gdaltest.post_reason( 'libkml_ds is none' )
        return 'faii'

    lyr = ogrtest.kml_ds.GetLayerByName('Placemarks')
    lyr.ResetReading()
    feat = lyr.GetNextFeature()

    wkt = 'POINT(-122.0822035425683 37.42228990140251)'
    
    if ogrtest.check_feature_geometry( feat, wkt):
        return 'fail'

    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' )
        return 'fail'
    
    wkt = 'POINT(-122.084075 37.4220033612141 50)'
    
    if ogrtest.check_feature_geometry( feat, wkt):
        return 'fail'
    
    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' )
        return 'fail'
    
    wkt = 'POINT(-122.0857667006183 37.42156927867553 50)'
    
    if ogrtest.check_feature_geometry( feat, wkt):
        return 'fail'

    return 'success'

###############################################################################
# Test reading of KML linestring geometry
#
def ogr_libkml_linestring_read():
    
    if not ogrtest.have_read_libkml:
        return 'skip'

    if ogrtest.kml_ds is None:
        gdaltest.post_reason( 'libkml_ds is none' )
        return 'faii'

    lyr = ogrtest.kml_ds.GetLayerByName('Paths')
    lyr.ResetReading()
    feat = lyr.GetNextFeature()

    wkt = 'LINESTRING (-112.081423783034495 36.106778704771372 0, -112.087026775269294 36.0905099328766 0)'
    if ogrtest.check_feature_geometry(feat, wkt):
        return 'fail'
    
    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' )
        return 'fail'
 
    wkt = 'LINESTRING (-112.080622229594994 36.106734600079953 0,-112.085242575314993 36.090495986124218 0)'
    if ogrtest.check_feature_geometry(feat, wkt):
        return 'fail'
    
    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' % fid )
        return 'fail'
 
    wkt = 'LINESTRING (-112.265654928602004 36.094476726025462 2357,-112.266038452823807 36.093426088386707 2357,-112.266813901345301 36.092510587768807 2357,-112.267782683444494 36.091898273579957 2357,-112.268855751095202 36.091313794118697 2357,-112.269481071721899 36.090367720752099 2357,-112.269526855561097 36.089321714872852 2357,-112.269014456727604 36.088509160604723 2357,-112.268152881533894 36.087538135979557 2357,-112.2670588176031 36.086826852625677 2357,-112.265737458732104 36.086463123013033 2357)'
    if ogrtest.check_feature_geometry(feat, wkt):
        return 'fail'
    
    return 'success'

###############################################################################
# Test reading of KML polygon geometry
#
def ogr_libkml_polygon_read():
    
    if not ogrtest.have_read_libkml:
        return 'skip'

    if ogrtest.kml_ds is None:
        gdaltest.post_reason( 'libkml_ds is none' )
        return 'faii'

    lyr = ogrtest.kml_ds.GetLayerByName('Google Campus')
    lyr.ResetReading()
    feat = lyr.GetNextFeature()

    wkt = 'POLYGON ((-122.084893845961204 37.422571240447859 17,-122.084958097919795 37.422119226268563 17,-122.084746957304702 37.42207183952619 17,-122.084572538096197 37.422090067296757 17,-122.084595488672306 37.422159327008949 17,-122.0838521118269 37.422272785643713 17,-122.083792243334997 37.422035391120843 17,-122.0835076656616 37.422090069571063 17,-122.083470946415204 37.422009873951609 17,-122.083122108574798 37.422104649494599 17,-122.082924737457205 37.422265039903863 17,-122.082933916938501 37.422312428430942 17,-122.083383735973698 37.422250460876178 17,-122.083360785424802 37.422341592287452 17,-122.083420455164202 37.42237075460644 17,-122.083659133885007 37.422512920110009 17,-122.083975843895203 37.422658730937812 17,-122.084237474333094 37.422651439725207 17,-122.0845036949503 37.422651438643499 17,-122.0848020460801 37.422611339163147 17,-122.084788275051494 37.422563950551208 17,-122.084893845961204 37.422571240447859 17))'
    if ogrtest.check_feature_geometry( feat, wkt):
        return 'fail'
    
    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' % fid )
        return 'fail'
 
    wkt = 'POLYGON ((-122.085741277148301 37.422270331552568 17,-122.085816976848093 37.422314088323461 17,-122.085852582875006 37.422303374697442 17,-122.085879994563896 37.422256861387893 17,-122.085886010140896 37.422231107613797 17,-122.085806915728796 37.422202501738553 17,-122.085837954265301 37.42214027058678 17,-122.085673264051906 37.422086902144081 17,-122.085602292640701 37.42214885429042 17,-122.085590277843593 37.422128290487002 17,-122.085584167223701 37.422081719672462 17,-122.085485206574106 37.42210455874995 17,-122.085506726435199 37.422142679498243 17,-122.085443071291493 37.422127838461719 17,-122.085099071490404 37.42251282407603 17,-122.085676981863202 37.422818153236513 17,-122.086016227378295 37.422449188587223 17,-122.085726032700407 37.422292396042529 17,-122.085741277148301 37.422270331552568 17))'
    if ogrtest.check_feature_geometry(feat, wkt):
        return 'fail'
    
    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' % fid )
        return 'fail'
 
    wkt = 'POLYGON ((-122.085786228724203 37.421362088869692 25,-122.085731299060299 37.421369359894811 25,-122.085731299291794 37.421409349109027 25,-122.085607707367899 37.421383901665649 25,-122.085580242651602 37.42137299550869 25,-122.085218622197104 37.421372995043157 25,-122.085227776563897 37.421616565082651 25,-122.085259818934702 37.421605658944031 25,-122.085259818549901 37.421682001560001 25,-122.085236931147804 37.421700178603459 25,-122.085264395782801 37.421761979825753 25,-122.085323903274599 37.421761980139067 25,-122.085355945432397 37.421852864451999 25,-122.085410875246296 37.421889218237339 25,-122.085479537935697 37.42189285337048 25,-122.085543622981902 37.421889217975462 25,-122.085626017804202 37.421860134999257 25,-122.085937287963006 37.421860134536047 25,-122.085942871866607 37.42160898590042 25,-122.085965546986102 37.421579927591438 25,-122.085864046234093 37.421471150029568 25,-122.0858548911215 37.421405713261841 25,-122.085809116276806 37.4214057134039 25,-122.085786228724203 37.421362088869692 25))'
    if ogrtest.check_feature_geometry(feat, wkt):
        return 'fail'
    
    feat = lyr.GetNextFeature()
    if feat is None:
        gdaltest.post_reason( 'expected feature not found.' % fid )
        return 'fail'
 
    wkt = 'POLYGON ((-122.084437112828397 37.421772530030907 19,-122.084511885574599 37.421911115428962 19,-122.0850470999805 37.421787551215353 19,-122.085071991339106 37.421436630231611 19,-122.084916406231997 37.421372378221157 19,-122.084219386816699 37.421372378016258 19,-122.084219386589993 37.421476171614962 19,-122.083808641999099 37.4214613409357 19,-122.083789972856394 37.421313064107963 19,-122.083279653469802 37.421293288405927 19,-122.083260981920702 37.421392139442979 19,-122.082937362173695 37.421372363998763 19,-122.082906242566693 37.421515697788713 19,-122.082850226966499 37.421762825764652 19,-122.082943578863507 37.421767769696352 19,-122.083217411188002 37.421792485526858 19,-122.0835970430103 37.421748007445601 19,-122.083945555677104 37.421693642376027 19,-122.084007789463698 37.421762838158529 19,-122.084113587521003 37.421748011043917 19,-122.084076247378405 37.421713412923751 19,-122.084144704773905 37.421678815345693 19,-122.084144704222993 37.421817206601972 19,-122.084250333307395 37.421817070044597 19,-122.084437112828397 37.421772530030907 19))'
    if ogrtest.check_feature_geometry(feat, wkt):
        return 'fail'
 
    return 'success'

###############################################################################
# Write test

def ogr_libkml_write(filename):

    if ogrtest.kml_drv is None:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource(filename)

    if filename != '/vsimem/libkml_use_doc_off.kmz':
        srs = osr.SpatialReference()
        srs.SetWellKnownGeogCS('WGS72')
        lyr = ds.CreateLayer('test_wgs72', srs = srs)

        dst_feat = ogr.Feature( lyr.GetLayerDefn() )
        dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT (2 49)'))
        if lyr.CreateFeature( dst_feat ) != 0:
            gdaltest.post_reason('CreateFeature failed.')
            return 'fail'
        if dst_feat.GetGeometryRef().ExportToWkt() != 'POINT (2 49)':
            print(dst_feat.GetGeometryRef().ExportToWkt())
            gdaltest.post_reason('CreateFeature changed the geometry.')
            return 'fail'
        dst_feat.Destroy()

    lyr = ds.CreateLayer('test_wgs84')

    fieldefn = ogr.FieldDefn ( 'name', ogr.OFTString)
    lyr.CreateField(fieldefn)
    fieldefn = ogr.FieldDefn ( 'description', ogr.OFTString)
    lyr.CreateField(fieldefn)
    fieldefn = ogr.FieldDefn ( 'foo', ogr.OFTString)
    lyr.CreateField(fieldefn)

    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetField('name', 'my_name')
    dst_feat.SetField('description', 'my_description')
    dst_feat.SetField('foo', 'bar')
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT (2 49)'))
    if lyr.CreateFeature( dst_feat ) != 0:
        gdaltest.post_reason('CreateFeature failed.')
        return 'fail'
    dst_feat.Destroy()

    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT (2 49 1)'))
    if lyr.CreateFeature( dst_feat ) != 0:
        gdaltest.post_reason('CreateFeature failed.')
        return 'fail'
    dst_feat.Destroy()

    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('LINESTRING (0 1,2 3)'))
    if lyr.CreateFeature( dst_feat ) != 0:
        gdaltest.post_reason('CreateFeature failed.')
        return 'fail'
    dst_feat.Destroy()

    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('POLYGON ((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0),(0.25 0.25 0,0.25 0.75 0,0.75 0.75 0,0.75 0.25 0,0.25 0.25 0))'))
    if lyr.CreateFeature( dst_feat ) != 0:
        gdaltest.post_reason('CreateFeature failed.')
        return 'fail'
    dst_feat.Destroy()

    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('MULTIPOINT (2 49,2 49)'))
    if lyr.CreateFeature( dst_feat ) != 0:
        gdaltest.post_reason('CreateFeature failed.')
        return 'fail'
    dst_feat.Destroy()

    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('MULTILINESTRING ((0 1,2 3),(0 1,2 3))'))
    if lyr.CreateFeature( dst_feat ) != 0:
        gdaltest.post_reason('CreateFeature failed.')
        return 'fail'
    dst_feat.Destroy()

    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('MULTIPOLYGON (((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0),(0.25 0.25 0,0.25 0.75 0,0.75 0.75 0,0.75 0.25 0,0.25 0.25 0)),((-0.25 0.25 0,-0.25 0.75 0,-0.75 0.75 0,-0.75 0.25 0,-0.25 0.25 0)))'))
    if lyr.CreateFeature( dst_feat ) != 0:
        gdaltest.post_reason('CreateFeature failed.')
        return 'fail'
    dst_feat.Destroy()

    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('GEOMETRYCOLLECTION (POINT (2 49 1),LINESTRING (0 1,2 3))'))
    if lyr.CreateFeature( dst_feat ) != 0:
        gdaltest.post_reason('CreateFeature failed.')
        return 'fail'
    dst_feat.Destroy()

    ds.Destroy()

    return 'success'

###############################################################################
# Check previous test

def ogr_libkml_check_write(filename):

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open(filename)
    if filename != '/vsimem/libkml_use_doc_off.kmz':
        lyr = ds.GetLayerByName('test_wgs84')
    else:
        lyr = ds.GetLayer(0)
    if lyr.GetFeatureCount() != 8:
        gdaltest.post_reason('Bad feature count.')
        return 'fail'

    feat = lyr.GetNextFeature()
    if feat.GetField('name') != 'my_name':
        print(feat.GetField('name'))
        gdaltest.post_reason('Unexpected name.')
        return 'fail'
    if feat.GetField('description') != 'my_description':
        print(feat.GetField('description'))
        gdaltest.post_reason('Unexpected description.')
        return 'fail'
    if feat.GetField('foo') != 'bar':
        print(feat.GetField('foo'))
        gdaltest.post_reason('Unexpected foo.')
        return 'fail'
    if feat.GetGeometryRef().ExportToWkt() != 'POINT (2 49 0)':
        print(feat.GetGeometryRef().ExportToWkt())
        gdaltest.post_reason('Unexpected geometry.')
        return 'fail'
    feat.Destroy()

    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'POINT (2 49 1)':
        print(feat.GetGeometryRef().ExportToWkt())
        gdaltest.post_reason('Unexpected geometry.')
        return 'fail'
    feat.Destroy()

    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'LINESTRING (0 1 0,2 3 0)':
        print(feat.GetGeometryRef().ExportToWkt())
        gdaltest.post_reason('Unexpected geometry.')
        return 'fail'
    feat.Destroy()

    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'POLYGON ((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0),(0.25 0.25 0,0.25 0.75 0,0.75 0.75 0,0.75 0.25 0,0.25 0.25 0))':
        print(feat.GetGeometryRef().ExportToWkt())
        gdaltest.post_reason('Unexpected geometry.')
        return 'fail'
    feat.Destroy()

    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'MULTIPOINT (2 49 0,2 49 0)':
        print(feat.GetGeometryRef().ExportToWkt())
        gdaltest.post_reason('Unexpected geometry.')
        return 'fail'
    feat.Destroy()

    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'MULTILINESTRING ((0 1 0,2 3 0),(0 1 0,2 3 0))':
        print(feat.GetGeometryRef().ExportToWkt())
        gdaltest.post_reason('Unexpected geometry.')
        return 'fail'
    feat.Destroy()

    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'MULTIPOLYGON (((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0),(0.25 0.25 0,0.25 0.75 0,0.75 0.75 0,0.75 0.25 0,0.25 0.25 0)),((-0.25 0.25 0,-0.25 0.75 0,-0.75 0.75 0,-0.75 0.25 0,-0.25 0.25 0)))':
        print(feat.GetGeometryRef().ExportToWkt())
        gdaltest.post_reason('Unexpected geometry.')
        return 'fail'
    feat.Destroy()

    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'GEOMETRYCOLLECTION (POINT (2 49 1),LINESTRING (0 1 0,2 3 0))':
        print(feat.GetGeometryRef().ExportToWkt())
        gdaltest.post_reason('Unexpected geometry.')
        return 'fail'
    feat.Destroy()

    ds.Destroy()

    return 'success'

###############################################################################
def ogr_libkml_write_kml():
    return ogr_libkml_write('/vsimem/libkml.kml')

def ogr_libkml_check_write_kml():
    return ogr_libkml_check_write('/vsimem/libkml.kml')

def ogr_libkml_write_kmz():
    return ogr_libkml_write('/vsimem/libkml.kmz')

def ogr_libkml_check_write_kmz():
    return ogr_libkml_check_write('/vsimem/libkml.kmz')

def ogr_libkml_write_kmz_use_doc_off():
    gdal.SetConfigOption("LIBKML_USE_DOC.KML", "NO")
    ret = ogr_libkml_write('/vsimem/libkml_use_doc_off.kmz')
    gdal.SetConfigOption("LIBKML_USE_DOC.KML", None)
    return ret

def ogr_libkml_check_write_kmz_use_doc_off():
    return ogr_libkml_check_write('/vsimem/libkml_use_doc_off.kmz')

def ogr_libkml_write_dir():
    return ogr_libkml_write('/vsimem/libkmldir')

def ogr_libkml_check_write_dir():
    if not ogrtest.have_read_libkml:
        return 'skip'
        
    ret = ogr_libkml_check_write('/vsimem/libkmldir')
    files = gdal.ReadDir('/vsimem/libkmldir')
    for filename in files:
        gdal.Unlink('/vsimem/libkmldir/' + filename)
    gdal.Rmdir('/vsimem/libkmldir')
    return ret

###############################################################################
# Test reading attributes with XML content in them
#
def ogr_libkml_xml_attributes():
    
    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open('data/description_with_xml.kml')

    lyr = ds.GetLayer(0)
    feat = lyr.GetNextFeature()

    if feat.GetField('description').find('Description&lt;br&gt;&lt;/br&gt;&lt;i attr="val"&gt;Interesting&lt;/i&gt;&lt;br&gt;&lt;/br&gt;') != 0:
        gdaltest.post_reason( 'Wrong description field value' )
        print('got: %s ' % feat.GetField('description'))
        return 'fail'

    feat.Destroy()
    ds.Destroy()

    return 'success'

###############################################################################
# Test reading all geometry types (#3558)
#
def ogr_libkml_read_geometries():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open('data/geometries.kml')

    lyr = ds.GetLayer(0)
    feat = lyr.GetNextFeature()
    while feat is not None:
        feat.Destroy()
        feat = lyr.GetNextFeature()

    ds.Destroy()

    return 'success'

###############################################################################
# Run test_ogrsf

def ogr_libkml_test_ogrsf():

    if not ogrtest.have_read_libkml:
        return 'skip'

    import test_cli_utilities
    if test_cli_utilities.get_test_ogrsf_path() is None:
        return 'skip'

    ret = gdaltest.runexternal(test_cli_utilities.get_test_ogrsf_path() + ' --config OGR_SKIP KML -ro data/samples.kml')

    if ret.find("using driver `LIBKML'") == -1 or ret.find('INFO') == -1 or ret.find('ERROR') != -1:
        print(ret)
        return 'fail'

    return 'success'

###############################################################################
# Test reading KML with only Placemark

def ogr_libkml_read_placemark():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open('data/placemark.kml')
    lyr = ds.GetLayer(0)
    feat = lyr.GetNextFeature()
    if feat is None:
        return 'fail'

    ds = None

    return 'success'

###############################################################################
# Test reading KML without any layer

def ogr_libkml_read_empty():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open('data/empty.kml')
    if ds.GetLayerCount() != 0:
        gdaltest.post_reason('failed')
        print(ds.GetLayerCount())
        return 'fail'

    ds = None

    return 'success'

###############################################################################
# Test reading KML with empty layers

def ogr_libkml_read_emptylayers():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open('data/emptylayers.kml')
    if ds.GetLayerCount() != 2:
        gdaltest.post_reason('failed')
        print(ds.GetLayerCount())
        return 'fail'

    # --&gt; One difference with the old KML driver
    if ds.GetLayer(0).GetFeatureCount() != 1:
        gdaltest.post_reason('failed')
        print(ds.GetLayer(0).GetFeatureCount())
        return 'fail'

    if ds.GetLayer(1).GetFeatureCount() != 0:
        gdaltest.post_reason('failed')
        print(ds.GetLayer(1).GetFeatureCount())
        return 'fail'

    ds = None

    return 'success'

###############################################################################
# Test reading KML with empty layers

def ogr_libkml_read_schema():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open('data/test_schema.kml')
    if ds.GetLayerCount() != 4:
        gdaltest.post_reason('failed')
        print(ds.GetLayerCount())
        return 'fail'

    lyr = ds.GetLayer(0)
    feat = lyr.GetNextFeature()
    if feat.GetField('foo') != 'bar':
        gdaltest.post_reason('failed')
        feat.DumpReadable()
        return 'fail'

    lyr = ds.GetLayer(1)
    feat = lyr.GetNextFeature()
    if feat.GetField('foo') != 'baz':
        gdaltest.post_reason('failed')
        feat.DumpReadable()
        return 'fail'

    lyr = ds.GetLayer(2)
    if lyr.GetLayerDefn().GetFieldIndex('foo') != -1:
        gdaltest.post_reason('failed')
        return 'fail'

    lyr = ds.GetLayer(3)
    if lyr.GetLayerDefn().GetFieldIndex('foo') != -1:
        gdaltest.post_reason('failed')
        return 'fail'

    ds = None

    return 'success'

###############################################################################
# Test reading KML with &lt;Data&gt; elements of &lt;ExtendedData&gt; in case
# &lt;ExtendedData&gt; doesn't use a &lt;SchemaData&gt; (test changeset r22127)

def ogr_libkml_extended_data_without_schema_data():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open('data/extended_data_without_schema_data.kml')
    lyr = ds.GetLayer(0)
    feat = lyr.GetNextFeature()
    if feat.GetField('field1') != '1_1':
        gdaltest.post_reason('failed')
        feat.DumpReadable()
        return 'fail'
    if feat.GetField('field2') != '1_2':
        gdaltest.post_reason('failed')
        feat.DumpReadable()
        return 'fail'

    feat = lyr.GetNextFeature()
    if feat.GetField('field1') != '2_1':
        gdaltest.post_reason('failed')
        feat.DumpReadable()
        return 'fail'
    if feat.IsFieldSet('field2'):
        gdaltest.post_reason('failed')
        feat.DumpReadable()
        return 'fail'

    ds = None

    return 'success'

###############################################################################
# Test reading KML with &lt;gx:Track&gt; element (#5095)

def ogr_libkml_gxtrack():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open('data/gxtrack.kml')
    lyr = ds.GetLayer(0)

    feat = lyr.GetNextFeature()
    if feat.GetField('begin') != '2013/05/28 12:00:00' or \
       feat.GetField('end') != '2013/05/28 13:00:00' or \
       feat.GetGeometryRef().ExportToWkt() != 'LINESTRING (2 49,3 50)':
        feat.DumpReadable()
        gdaltest.post_reason('failure')
        return 'fail'
    ds = None

    return 'success'

###############################################################################
# Test generating and reading KML with &lt;Camera&gt; element

def ogr_libkml_camera():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_camera.kml")
    lyr = ds.CreateLayer('test')
    lyr.CreateField(ogr.FieldDefn("heading", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("tilt", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("roll", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("altitudeMode", ogr.OFTString))
    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT (2 49)'))
    dst_feat.SetField("heading", 70)
    dst_feat.SetField("tilt", 75)
    dst_feat.SetField("roll", 10)
    lyr.CreateFeature( dst_feat )
    
    dst_feat = ogr.Feature( lyr.GetLayerDefn() )
    dst_feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT (3 50 1)'))
    dst_feat.SetField("heading", -70)
    dst_feat.SetField("altitudeMode", "relativeToGround")
    lyr.CreateFeature( dst_feat )

    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_camera.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;Camera&gt;') == -1 or \
       data.find('&lt;longitude&gt;2&lt;/longitude&gt;') == -1 or \
       data.find('&lt;latitude&gt;49&lt;/latitude&gt;') == -1 or \
       data.find('&lt;heading&gt;70&lt;/heading&gt;') == -1 or \
       data.find('&lt;tilt&gt;75&lt;/tilt&gt;') == -1 or \
       data.find('&lt;roll&gt;10&lt;/roll&gt;') == -1 or \
       data.find('&lt;altitudeMode&gt;relativeToGround&lt;/altitudeMode&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    ds = ogr.Open('/vsimem/ogr_libkml_camera.kml')
    lyr = ds.GetLayer(0)

    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'POINT (2 49 0)' or \
       feat.GetField("heading") != 70.0 or \
       feat.GetField("tilt") != 75.0 or \
       feat.GetField("roll") != 10.0:
        feat.DumpReadable()
        gdaltest.post_reason('failure')
        return 'fail'

    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'POINT (3 50 1)' or \
       feat.GetField("heading") != -70.0 or \
       feat.IsFieldSet("tilt") or \
       feat.IsFieldSet("roll") or \
       feat.GetField("altitudeMode") != 'relativeToGround':
        feat.DumpReadable()
        gdaltest.post_reason('failure')
        return 'fail'
    ds = None

    return 'success'

###############################################################################
# Test generating a LookAt element at Document level

def ogr_libkml_write_layer_lookat():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_layer_lookat.kml")
    options = [ 'LOOKAT_LONGITUDE=2', 'LOOKAT_LATITUDE=49', 'LOOKAT_RANGE=150' ]
    lyr = ds.CreateLayer('test', options = options)
    options = [ 'LOOKAT_LONGITUDE=3', 'LOOKAT_LATITUDE=50', 'LOOKAT_RANGE=250',
                'LOOKAT_ALTITUDE=100', 'LOOKAT_HEADING=70', 'LOOKAT_TILT=50', 'LOOKAT_ALTITUDEMODE=relativeToGround']
    lyr = ds.CreateLayer('test2', options = options)
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_layer_lookat.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;LookAt&gt;') == -1 or \
       data.find('&lt;longitude&gt;2&lt;/longitude&gt;') == -1 or \
       data.find('&lt;latitude&gt;49&lt;/latitude&gt;') == -1 or \
       data.find('&lt;range&gt;150&lt;/range&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    if data.find('&lt;LookAt&gt;') == -1 or \
       data.find('&lt;longitude&gt;3&lt;/longitude&gt;') == -1 or \
       data.find('&lt;latitude&gt;50&lt;/latitude&gt;') == -1 or \
       data.find('&lt;altitude&gt;100&lt;/altitude&gt;') == -1 or \
       data.find('&lt;heading&gt;70&lt;/heading&gt;') == -1 or \
       data.find('&lt;tilt&gt;50&lt;/tilt&gt;') == -1 or \
       data.find('&lt;range&gt;150&lt;/range&gt;') == -1 or \
       data.find('&lt;altitudeMode&gt;relativeToGround&lt;/altitudeMode&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'


###############################################################################
# Test generating a Camera element at Document level

def ogr_libkml_write_layer_camera():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_layer_camera.kml")
    options = [ 'CAMERA_LONGITUDE=3', 'CAMERA_LATITUDE=50', 'CAMERA_ALTITUDE=100',
                'CAMERA_HEADING=70', 'CAMERA_TILT=50', 'CAMERA_ROLL=10', 'CAMERA_ALTITUDEMODE=relativeToGround']
    lyr = ds.CreateLayer('test', options = options)
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_layer_camera.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;Camera&gt;') == -1 or \
       data.find('&lt;longitude&gt;3&lt;/longitude&gt;') == -1 or \
       data.find('&lt;latitude&gt;50&lt;/latitude&gt;') == -1 or \
       data.find('&lt;altitude&gt;100&lt;/altitude&gt;') == -1 or \
       data.find('&lt;heading&gt;70&lt;/heading&gt;') == -1 or \
       data.find('&lt;tilt&gt;50&lt;/tilt&gt;') == -1 or \
       data.find('&lt;roll&gt;10&lt;/roll&gt;') == -1 or \
       data.find('&lt;altitudeMode&gt;relativeToGround&lt;/altitudeMode&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing MultiGeometry

def ogr_libkml_write_multigeometry():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_multigeometry.kml")
    lyr = ds.CreateLayer('test')
    feat = ogr.Feature(lyr.GetLayerDefn())
    # Transformed into POINT per ATC 66
    feat.SetGeometry(ogr.CreateGeometryFromWkt('MULTIPOINT(0 1)'))
    lyr.CreateFeature(feat)
    feat = ogr.Feature(lyr.GetLayerDefn())
    # Warning emitted per ATC 66
    feat.SetGeometry(ogr.CreateGeometryFromWkt('MULTIPOINT EMPTY'))
    lyr.CreateFeature(feat)

    ds = None
    
    ds = ogr.Open("/vsimem/ogr_libkml_write_multigeometry.kml")
    lyr = ds.GetLayer(0)
    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'POINT (0 1 0)':
        feat.DumpReadable()
        gdaltest.post_reason('failure')
        return 'fail'
    feat = lyr.GetNextFeature()
    if feat.GetGeometryRef().ExportToWkt() != 'GEOMETRYCOLLECTION EMPTY':
        feat.DumpReadable()
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing &lt;snippet&gt;

def ogr_libkml_write_snippet():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_snippet.kml")
    lyr = ds.CreateLayer('test')
    lyr.CreateField(ogr.FieldDefn("snippet", ogr.OFTString))
    feat = ogr.Feature(lyr.GetLayerDefn())
    feat.SetField('snippet', 'test_snippet')
    feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 1)'))
    lyr.CreateFeature(feat)
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_snippet.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;snippet&gt;test_snippet&lt;/snippet&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    ds = ogr.Open("/vsimem/ogr_libkml_write_snippet.kml")
    lyr = ds.GetLayer(0)
    feat = lyr.GetNextFeature()
    if feat.GetField('snippet') != 'test_snippet':
        feat.DumpReadable()
        gdaltest.post_reason('failure')
        return 'fail'
    if feat.GetGeometryRef().ExportToWkt() != 'POINT (0 1 0)':
        feat.DumpReadable()
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing &lt;atom:author&gt;

def ogr_libkml_write_atom_author():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_atom_author.kml",
                                                        options = ['author_name=name', 'author_uri=http://foo', 'author_email=foo@bar.com'])
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_atom_author.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;kml xmlns="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom"&gt;') == -1 or \
       data.find('&lt;atom:name&gt;name&lt;/atom:name&gt;') == -1 or \
       data.find('&lt;atom:uri&gt;http://foo&lt;/atom:uri&gt;') == -1 or \
       data.find('&lt;atom:email&gt;foo@bar.com&lt;/atom:email&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing &lt;atom:link&gt;

def ogr_libkml_write_atom_link():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_atom_link.kml",
                                                        options = ['link=http://foo'])
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_atom_link.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;kml xmlns="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom"&gt;') == -1 or \
       data.find('&lt;atom:link href="http://foo" rel="related"/&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing &lt;phoneNumber&gt;

def ogr_libkml_write_phonenumber():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_phonenumber.kml",
                                                        options = ['phonenumber=tel:911'])
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_phonenumber.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;phoneNumber&gt;tel:911&lt;/phoneNumber&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing Region

def ogr_libkml_write_region():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_region.kml")
    lyr = ds.CreateLayer('auto', options = ['ADD_REGION=YES'])
    feat = ogr.Feature(lyr.GetLayerDefn())
    feat.SetGeometry(ogr.CreateGeometryFromWkt('POLYGON((2 48,2 49,3 49,3 48,2 48))'))
    lyr.CreateFeature(feat)
    lyr = ds.CreateLayer('manual', options = ['ADD_REGION=YES', 'REGION_XMIN=-180', \
        'REGION_XMAX=180', 'REGION_YMIN=-90', 'REGION_YMAX=90', \
        'REGION_MIN_LOD_PIXELS=128', 'REGION_MAX_LOD_PIXELS=10000000', \
        'REGION_MIN_FADE_EXTENT=1', 'REGION_MAX_FADE_EXTENT=2' ])
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_region.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;north&gt;49&lt;/north&gt;') == -1 or \
       data.find('&lt;south&gt;48&lt;/south&gt;') == -1 or \
       data.find('&lt;east&gt;3&lt;/east&gt;') == -1 or \
       data.find('&lt;west&gt;2&lt;/west&gt;') == -1 or \
       data.find('&lt;minLodPixels&gt;256&lt;/minLodPixels&gt;') == -1 or \
       data.find('&lt;maxLodPixels&gt;-1&lt;/maxLodPixels&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    if data.find('&lt;north&gt;90&lt;/north&gt;') == -1 or \
       data.find('&lt;south&gt;-90&lt;/south&gt;') == -1 or \
       data.find('&lt;east&gt;180&lt;/east&gt;') == -1 or \
       data.find('&lt;west&gt;-180&lt;/west&gt;') == -1 or \
       data.find('&lt;minLodPixels&gt;128&lt;/minLodPixels&gt;') == -1 or \
       data.find('&lt;maxLodPixels&gt;10000000&lt;/maxLodPixels&gt;') == -1 or \
       data.find('&lt;minFadeExtent&gt;1&lt;/minFadeExtent&gt;') == -1 or \
       data.find('&lt;maxFadeExtent&gt;2&lt;/maxFadeExtent&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'
        
    return 'success'

###############################################################################
# Test writing ScreenOverlay

def ogr_libkml_write_screenoverlay():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_screenoverlay.kml")
    lyr = ds.CreateLayer('auto', options = ['SO_HREF=http://foo'])
    lyr = ds.CreateLayer('manual', options = ['SO_HREF=http://bar',
                                              'SO_NAME=name',
                                              'SO_DESCRIPTION=description',
                                              'SO_OVERLAY_X=10',
                                              'SO_OVERLAY_Y=20',
                                              'SO_OVERLAY_XUNITS=pixels',
                                              'SO_OVERLAY_YUNITS=pixels',
                                              'SO_SCREEN_X=0.4',
                                              'SO_SCREEN_Y=0.5',
                                              'SO_SCREEN_XUNITS=fraction',
                                              'SO_SCREEN_YUNITS=fraction',
                                              'SO_SIZE_X=1.1',
                                              'SO_SIZE_Y=1.2',
                                              'SO_SIZE_XUNITS=fraction',
                                              'SO_SIZE_YUNITS=fraction'])
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_screenoverlay.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;href&gt;http://foo&lt;/href&gt;') == -1 or \
       data.find('&lt;screenXY x="0.05" xunits="fraction" y="0.05" yunits="fraction"/&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    if data.find('&lt;overlayXY x="10" xunits="pixels" y="20" yunits="pixels"/&gt;') == -1 or \
       data.find('&lt;screenXY x="0.4" xunits="fraction" y="0.5" yunits="fraction"/&gt;') == -1 or \
       data.find('&lt;size x="1.1" xunits="fraction" y="1.2" yunits="fraction"/&gt;') == -1 or \
       data.find('&lt;name&gt;name&lt;/name&gt;') == -1 or \
       data.find('&lt;description&gt;description&lt;/description&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing Model

def ogr_libkml_write_model():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_model.kml")
    lyr = ds.CreateLayer('test')
    lyr.CreateField(ogr.FieldDefn("model", ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("heading", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("tilt", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("roll", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("altitudeMode", ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("scale_x", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("scale_y", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("scale_z", ogr.OFTReal))

    feat = ogr.Feature( lyr.GetLayerDefn() )
    feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT (2 49 10)'))
    feat.SetField("tilt", 75)
    feat.SetField("roll", 10)
    feat.SetField("heading", -70)
    feat.SetField("scale_x", 2)
    feat.SetField("scale_y", 3)
    feat.SetField("scale_z", 4)
    feat.SetField("altitudeMode", "relativeToGround")
    feat.SetField("model", "http://makc.googlecode.com/svn/trunk/flash/sandy_flar2/cube.dae")
    lyr.CreateFeature(feat)

    feat = ogr.Feature( lyr.GetLayerDefn() )
    feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT (2 49)'))
    feat.SetField("model", "http://foo")
    lyr.CreateFeature(feat)

    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_model.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;longitude&gt;2&lt;/longitude&gt;') == -1 or \
       data.find('&lt;latitude&gt;49&lt;/latitude&gt;') == -1 or \
       data.find('&lt;altitude&gt;10&lt;/altitude&gt;') == -1 or \
       data.find('&lt;altitudeMode&gt;relativeToGround&lt;/altitudeMode&gt;') == -1 or \
       data.find('&lt;heading&gt;-70&lt;/heading&gt;') == -1 or \
       data.find('&lt;tilt&gt;75&lt;/tilt&gt;') == -1 or \
       data.find('&lt;roll&gt;10&lt;/roll&gt;') == -1 or \
       data.find('&lt;x&gt;2&lt;/x&gt;') == -1 or \
       data.find('&lt;y&gt;3&lt;/y&gt;') == -1 or \
       data.find('&lt;z&gt;4&lt;/z&gt;') == -1 or \
       data.find('&lt;x&gt;1&lt;/x&gt;') == -1 or \
       data.find('&lt;y&gt;1&lt;/y&gt;') == -1 or \
       data.find('&lt;z&gt;1&lt;/z&gt;') == -1 or \
       data.find('&lt;href&gt;http://makc.googlecode.com/svn/trunk/flash/sandy_flar2/cube.dae&lt;/href&gt;') == -1 or \
       data.find('&lt;href&gt;http://foo&lt;/href&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    # This can only appear if HTTP ressource is available
    if data.find('&lt;targetHref&gt;http://makc.googlecode.com/svn/trunk/flash/sandy_flar2/cube.gif&lt;/targetHref&gt;') == -1 or \
       data.find('&lt;sourceHref&gt;cube.gif&lt;/sourceHref&gt;') == -1:

        if gdaltest.gdalurlopen('http://makc.googlecode.com/svn/trunk/flash/sandy_flar2/cube.dae') is not None:
            print(data)
            gdaltest.post_reason('failure')
            return 'fail'

    return 'success'

###############################################################################
# Test read / write of style

def ogr_libkml_read_write_style():

    if not ogrtest.have_read_libkml:
        return 'skip'

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_read_write_style_read.kml', 'wb')

    styles = """&lt;Style id="style1"&gt;
            &lt;IconStyle&gt;
                &lt;color&gt;01234567&lt;/color&gt;
                &lt;scale&gt;1.1&lt;/scale&gt;
                &lt;heading&gt;50&lt;/heading&gt;
                &lt;Icon&gt;
                    &lt;href&gt;http://style1&lt;/href&gt;
                &lt;/Icon&gt;
                &lt;hotSpot x="15" y="20"/&gt;
            &lt;/IconStyle&gt;
            &lt;LabelStyle&gt;
                &lt;color&gt;01234567&lt;/color&gt;
                &lt;scale&gt;1.1&lt;/scale&gt;
            &lt;/LabelStyle&gt;
            &lt;BalloonStyle&gt;
                &lt;bgColor&gt;ff00ffff&lt;/bgColor&gt;
                &lt;text&gt;&lt;![CDATA[This is $[name], whose description is:&lt;br/&gt;$[description]]]&gt;&lt;/text&gt;
            &lt;/BalloonStyle&gt;
        &lt;/Style&gt;
        &lt;Style id="style2"&gt;
            &lt;LineStyle&gt;
                &lt;color&gt;01234567&lt;/color&gt;
                &lt;width&gt;1&lt;/width&gt;
            &lt;/LineStyle&gt;
            &lt;PolyStyle&gt;
                &lt;color&gt;01234567&lt;/color&gt;
            &lt;/PolyStyle&gt;
        &lt;/Style&gt;"""

    content = """&lt;kml xmlns="http://www.opengis.net/kml/2.2"&gt;
    &lt;Document&gt;
        %s
        &lt;StyleMap id="styleMapExample"&gt;
            &lt;Pair&gt;
                &lt;key&gt;normal&lt;/key&gt;
                &lt;Style id="inline_style"&gt;
                    &lt;IconStyle&gt;
                        &lt;Icon&gt;
                            &lt;href&gt;http://inline_style&lt;/href&gt;
                        &lt;/Icon&gt;
                    &lt;/IconStyle&gt;
                &lt;/Style&gt;
            &lt;/Pair&gt;
            &lt;Pair&gt;
                &lt;key&gt;highlight&lt;/key&gt;
                &lt;styleUrl&gt;#style2&lt;/styleUrl&gt;
            &lt;/Pair&gt;
        &lt;/StyleMap&gt;
    &lt;/Document&gt;
    &lt;/kml&gt;""" % styles
    
    resolved_stylemap = """&lt;Style id="styleMapExample"&gt;
      &lt;IconStyle&gt;
        &lt;Icon&gt;
          &lt;href&gt;http://inline_style&lt;/href&gt;
        &lt;/Icon&gt;
      &lt;/IconStyle&gt;
    &lt;/Style&gt;"""

    resolved_stylemap_highlight = """&lt;Style id="styleMapExample"&gt;
        &lt;LineStyle&gt;
            &lt;color&gt;01234567&lt;/color&gt;
            &lt;width&gt;1&lt;/width&gt;
        &lt;/LineStyle&gt;
        &lt;PolyStyle&gt;
            &lt;color&gt;01234567&lt;/color&gt;
        &lt;/PolyStyle&gt;
    &lt;/Style&gt;"""
    gdal.VSIFWriteL(content, 1, len(content), f)
    gdal.VSIFCloseL(f)

    src_ds = ogr.Open('/vsimem/ogr_libkml_read_write_style_read.kml')
    style_table = src_ds.GetStyleTable()

    options = [ 'style1_balloonstyle_bgcolor=#FFFF00',
                'style1_balloonstyle_text=This is $[name], whose description is:&lt;br/&gt;$[description]']
    ds = ogr.GetDriverByName('LIBKML').CreateDataSource('/vsimem/ogr_libkml_read_write_style_write.kml', options = options)
    ds.SetStyleTable(style_table)
    ds = None
    src_ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_read_write_style_write.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)
    lines = [ l.strip() for l in data.split('\n')]

    lines_got = lines[lines.index('&lt;Style id="style1"&gt;'):lines.index('&lt;Style id="styleMapExample"&gt;')]
    lines_ref = [ l.strip() for l in styles.split('\n')]
    if lines_got != lines_ref:
        print(data)
        print(styles)
        gdaltest.post_reason('failure')
        return 'fail'

    lines_got = lines[lines.index('&lt;Style id="styleMapExample"&gt;'):lines.index('&lt;/Document&gt;')]
    lines_ref = [ l.strip() for l in resolved_stylemap.split('\n')]
    if lines_got != lines_ref:
        print(data)
        print(resolved_stylemap)
        gdaltest.post_reason('failure')
        return 'fail'

    # Test reading highlight style in StyleMap
    gdal.SetConfigOption('LIBKML_STYLEMAP_KEY', 'HIGHLIGHT')
    src_ds = ogr.Open('/vsimem/ogr_libkml_read_write_style_read.kml')
    style_table = src_ds.GetStyleTable()
    gdal.SetConfigOption('LIBKML_STYLEMAP_KEY', None)

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource('/vsimem/ogr_libkml_read_write_style_write.kml')
    ds.SetStyleTable(style_table)
    ds = None
    src_ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_read_write_style_write.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)
    lines = [ l.strip() for l in data.split('\n')]

    lines_got = lines[lines.index('&lt;Style id="styleMapExample"&gt;'):lines.index('&lt;/Document&gt;')]
    lines_ref = [ l.strip() for l in resolved_stylemap_highlight.split('\n')]
    if lines_got != lines_ref:
        print(data)
        print(resolved_stylemap_highlight)
        gdaltest.post_reason('failure')
        return 'fail'

    # Test writing feature style
    ds = ogr.GetDriverByName('LIBKML').CreateDataSource('/vsimem/ogr_libkml_read_write_style_write.kml')
    lyr = ds.CreateLayer('test')
    feat = ogr.Feature(lyr.GetLayerDefn())
    feat.SetStyleString('@unknown_style')
    lyr.CreateFeature(feat)
    feat = None
    feat = ogr.Feature(lyr.GetLayerDefn())
    style_string = 'PEN(c:#01234567,w:5.000000px);BRUSH(fc:#01234567);SYMBOL(id:"http://foo",a:50.000000,c:#01234567,s:1.100000);LABEL(c:#01234567,w:150.000000)'
    feat.SetStyleString(style_string)
    lyr.CreateFeature(feat)
    feat = None
    ds = None
    
    ds = ogr.Open('/vsimem/ogr_libkml_read_write_style_write.kml')
    lyr = ds.GetLayer(0)
    feat = lyr.GetNextFeature()
    if feat.GetStyleString() != '@unknown_style':
        print(feat.GetStyleString())
        gdaltest.post_reason('failure')
        return 'fail'
    feat = lyr.GetNextFeature()
    if feat.GetStyleString() != style_string:
        print(feat.GetStyleString())
        gdaltest.post_reason('failure')
        return 'fail'
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_read_write_style_write.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    expected_style = """&lt;Style&gt;
          &lt;IconStyle&gt;
            &lt;color&gt;67452301&lt;/color&gt;
            &lt;scale&gt;1.1&lt;/scale&gt;
            &lt;heading&gt;50&lt;/heading&gt;
            &lt;Icon&gt;
              &lt;href&gt;http://foo&lt;/href&gt;
            &lt;/Icon&gt;
          &lt;/IconStyle&gt;
          &lt;LabelStyle&gt;
            &lt;color&gt;67452301&lt;/color&gt;
            &lt;scale&gt;1.5&lt;/scale&gt;
          &lt;/LabelStyle&gt;
          &lt;LineStyle&gt;
            &lt;color&gt;67452301&lt;/color&gt;
            &lt;width&gt;5&lt;/width&gt;
          &lt;/LineStyle&gt;
          &lt;PolyStyle&gt;
            &lt;color&gt;67452301&lt;/color&gt;
          &lt;/PolyStyle&gt;
        &lt;/Style&gt;"""
    lines = [ l.strip() for l in data.split('\n')]

    lines_got = lines[lines.index('&lt;Style&gt;'):lines.index('&lt;/Style&gt;')+1]
    lines_ref = [ l.strip() for l in expected_style.split('\n')]
    if lines_got != lines_ref:
        print(data)
        print(resolved_stylemap_highlight)
        gdaltest.post_reason('failure')
        return 'fail'

    # Automatic StyleMap creation testing
    ds = ogr.GetDriverByName('LIBKML').CreateDataSource('/vsimem/ogr_libkml_read_write_style_write.kml')
    style_table = ogr.StyleTable()
    style_table.AddStyle('style1_normal','SYMBOL(id:"http://style1_normal",c:#67452301)')
    style_table.AddStyle('style1_highlight','SYMBOL(id:"http://style1_highlight",c:#10325476)')
    ds.SetStyleTable(style_table)
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_read_write_style_write.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)
    lines = [ l.strip() for l in data.split('\n')]

    expected_styles = """&lt;Style id="style1_normal"&gt;
      &lt;IconStyle&gt;
        &lt;color&gt;01234567&lt;/color&gt;
        &lt;Icon&gt;
          &lt;href&gt;http://style1_normal&lt;/href&gt;
        &lt;/Icon&gt;
      &lt;/IconStyle&gt;
    &lt;/Style&gt;
    &lt;Style id="style1_highlight"&gt;
      &lt;IconStyle&gt;
        &lt;color&gt;76543210&lt;/color&gt;
        &lt;Icon&gt;
          &lt;href&gt;http://style1_highlight&lt;/href&gt;
        &lt;/Icon&gt;
      &lt;/IconStyle&gt;
    &lt;/Style&gt;
    &lt;StyleMap id="style1"&gt;
      &lt;Pair&gt;
        &lt;key&gt;normal&lt;/key&gt;
        &lt;styleUrl&gt;#style1_normal&lt;/styleUrl&gt;
      &lt;/Pair&gt;
      &lt;Pair&gt;
        &lt;key&gt;highlight&lt;/key&gt;
        &lt;styleUrl&gt;#style1_highlight&lt;/styleUrl&gt;
      &lt;/Pair&gt;
    &lt;/StyleMap&gt;"""

    lines_got = lines[lines.index('&lt;Style id="style1_normal"&gt;'):lines.index('&lt;/StyleMap&gt;') + 1]
    lines_ref = [ l.strip() for l in expected_styles.split('\n')]
    if lines_got != lines_ref:
        print(data)
        print(styles)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing Update

def ogr_libkml_write_update():

    if not ogrtest.have_read_libkml:
        return 'skip'

    for i in range(3):

        if i == 0:
            name = "/vsimem/ogr_libkml_write_update.kml"
        elif i == 1:
            name = "/vsimem/ogr_libkml_write_update.kmz"
        else:
            name = "/vsimem/ogr_libkml_write_update_dir"

        ds = ogr.GetDriverByName('LIBKML').CreateDataSource(name,
                                                            options = [ 'UPDATE_TARGETHREF=http://foo'] )
        lyr = ds.CreateLayer('layer_to_edit')
        feat = ogr.Feature(lyr.GetLayerDefn())
        lyr.CreateFeature(feat)
        feat.SetFID(10)
        lyr.CreateFeature(feat)
        feat.SetFID(2)
        lyr.SetFeature(feat)
        lyr.DeleteFeature(3)
        ds = None

        if i == 0:
            f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_update.kml', 'rb')
        elif i == 1:
            f = gdal.VSIFOpenL('/vsizip//vsimem/ogr_libkml_write_update.kmz', 'rb')
        else:
            f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_update_dir/doc.kml', 'rb')
        if f is None:
            gdaltest.post_reason('failure')
            return 'fail'
        data = gdal.VSIFReadL(1, 2048, f)
        data = data.decode('ascii')
        gdal.VSIFCloseL(f)

        if data.find('&lt;NetworkLinkControl&gt;') == -1 or \
        data.find('&lt;Update&gt;') == -1 or \
        data.find('&lt;targetHref&gt;http://foo&lt;/targetHref&gt;') == -1 or \
        data.find('&lt;Placemark/&gt;') == -1 or \
        data.find('&lt;Placemark id="layer_to_edit.10"/&gt;') == -1 or \
        data.find('&lt;Create&gt;') == -1 or \
        data.find('&lt;Document targetId="layer_to_edit"&gt;') == -1 or \
        data.find('&lt;Change&gt;') == -1 or \
        data.find('&lt;Placemark targetId="layer_to_edit.2"/&gt;') == -1 or \
        data.find('&lt;Delete&gt;') == -1 or \
        data.find('&lt;Placemark targetId="layer_to_edit.3"/&gt;') == -1:
            print(data)
            gdaltest.post_reason('failure')
            return 'fail'

    return 'success'

###############################################################################
# Test writing NetworkLinkControl

def ogr_libkml_write_networklinkcontrol():

    if not ogrtest.have_read_libkml:
        return 'skip'

    options = [ 'NLC_MINREFRESHPERIOD=3600',
                'NLC_MAXSESSIONLENGTH=-1',
                'NLC_COOKIE=cookie',
                'NLC_MESSAGE=message',
                'NLC_LINKNAME=linkname',
                'NLC_LINKDESCRIPTION=linkdescription',
                'NLC_LINKSNIPPET=linksnippet',
                'NLC_EXPIRES=2014-12-31T23:59:59Z' ]

    for i in range(3):

        if i == 0:
            name = "/vsimem/ogr_libkml_write_networklinkcontrol.kml"
        elif i == 1:
            name = "/vsimem/ogr_libkml_write_networklinkcontrol.kmz"
        else:
            name = "/vsimem/ogr_libkml_write_networklinkcontrol_dir"

        ds = ogr.GetDriverByName('LIBKML').CreateDataSource(name, options = options)
        ds = None

        if i == 0:
            f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_networklinkcontrol.kml', 'rb')
        elif i == 1:
            f = gdal.VSIFOpenL('/vsizip//vsimem/ogr_libkml_write_networklinkcontrol.kmz', 'rb')
        else:
            f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_networklinkcontrol_dir/doc.kml', 'rb')
        if f is None:
            gdaltest.post_reason('failure')
            return 'fail'
        data = gdal.VSIFReadL(1, 2048, f)
        data = data.decode('ascii')
        gdal.VSIFCloseL(f)

        if data.find('&lt;minRefreshPeriod&gt;3600&lt;/minRefreshPeriod&gt;') == -1 or \
        data.find('&lt;maxSessionLength&gt;-1&lt;/maxSessionLength&gt;') == -1 or \
        data.find('&lt;cookie&gt;cookie&lt;/cookie&gt;') == -1 or \
        data.find('&lt;message&gt;message&lt;/message&gt;') == -1 or \
        data.find('&lt;linkName&gt;linkname&lt;/linkName&gt;') == -1 or \
        data.find('&lt;linkDescription&gt;linkdescription&lt;/linkDescription&gt;') == -1 or \
        data.find('&lt;linkSnippet&gt;linksnippet&lt;/linkSnippet&gt;') == -1 or \
        data.find('&lt;expires&gt;2014-12-31T23:59:59Z&lt;/expires&gt;') == -1:
            print(data)
            gdaltest.post_reason('failure')
            return 'fail'

    return 'success'

###############################################################################
# Test writing ListStyle

def ogr_libkml_write_liststyle():

    if not ogrtest.have_read_libkml:
        return 'skip'

    options = [ 'LISTSTYLE_ICON_HREF=http://www.gdal.org/gdalicon.png' ]
    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_liststyle.kml", options = options)
    lyr = ds.CreateLayer('test', options = [ 'LISTSTYLE_ICON_HREF=http://foo'] )
    lyr = ds.CreateLayer('test_check', options = [ 'LISTSTYLE_TYPE=check'] )
    lyr = ds.CreateLayer('test_radioFolder', options = [ 'LISTSTYLE_TYPE=radioFolder'] )
    lyr = ds.CreateLayer('test_checkOffOnly', options = [ 'LISTSTYLE_TYPE=checkOffOnly'] )
    lyr = ds.CreateLayer('test_checkHideChildren', options = [ 'LISTSTYLE_TYPE=checkHideChildren'] )
    lyr = ds.CreateLayer('test_error', options = [ 'LISTSTYLE_TYPE=error'] )
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_liststyle.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;styleUrl&gt;#root_doc_liststyle&lt;/styleUrl&gt;') == -1 or \
       data.find('&lt;Style id="root_doc_liststyle"&gt;') == -1 or \
       data.find('&lt;href&gt;http://www.gdal.org/gdalicon.png&lt;/href&gt;') == -1 or \
       data.find('&lt;styleUrl&gt;#test_liststyle&lt;/styleUrl&gt;') == -1 or \
       data.find('&lt;Style id="test_liststyle"&gt;') == -1 or \
       data.find('&lt;href&gt;http://foo&lt;/href&gt;') == -1 or \
       data.find('&lt;listItemType&gt;check&lt;/listItemType&gt;') == -1 or \
       data.find('&lt;listItemType&gt;radioFolder&lt;/listItemType&gt;') == -1 or \
       data.find('&lt;listItemType&gt;checkOffOnly&lt;/listItemType&gt;') == -1 or \
       data.find('&lt;listItemType&gt;checkHideChildren&lt;/listItemType&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing NetworkLink

def ogr_libkml_write_networklink():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_networklink.kml")
    lyr = ds.CreateLayer('test')
    lyr.CreateField(ogr.FieldDefn('name', ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("networklink", ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("networklink_refreshvisibility", ogr.OFTInteger))
    lyr.CreateField(ogr.FieldDefn("networklink_flytoview", ogr.OFTInteger))
    lyr.CreateField(ogr.FieldDefn("networklink_refreshMode", ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("networklink_refreshInterval", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("networklink_viewRefreshMode", ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("networklink_viewRefreshTime", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("networklink_viewBoundScale", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("networklink_viewFormat", ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("networklink_httpQuery", ogr.OFTString))

    feat = ogr.Feature(lyr.GetLayerDefn())
    feat.SetField("name", "a network link")
    feat.SetField("networklink", "http://developers.google.com/kml/documentation/Point.kml")
    feat.SetField("networklink_refreshVisibility", 1)
    feat.SetField("networklink_flyToView", 1)
    feat.SetField("networklink_refreshInterval", 60)
    feat.SetField("networklink_httpQuery", "[clientVersion]")
    lyr.CreateFeature(feat)

    feat = ogr.Feature(lyr.GetLayerDefn())
    feat.SetField("networklink", "http://developers.google.com/kml/documentation/Point.kml")
    feat.SetField("networklink_viewRefreshTime", 30)
    lyr.CreateFeature(feat)

    feat = ogr.Feature(lyr.GetLayerDefn())
    feat.SetField("networklink", "http://developers.google.com/kml/documentation/Point.kml")
    feat.SetField("networklink_refreshMode", 'onExpire')
    feat.SetField("networklink_viewRefreshMode", 'onRegion')
    feat.SetField("networklink_viewBoundScale", 0.5)
    feat.SetField("networklink_viewFormat", 'BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]')
    lyr.CreateFeature(feat)

    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_networklink.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;name&gt;a network link&lt;/name&gt;') == -1 or \
       data.find('&lt;refreshVisibility&gt;1&lt;/refreshVisibility&gt;') == -1 or \
       data.find('&lt;flyToView&gt;1&lt;/flyToView&gt;') == -1 or \
       data.find('&lt;href&gt;http://developers.google.com/kml/documentation/Point.kml&lt;/href&gt;') == -1 or \
       data.find('&lt;refreshMode&gt;onInterval&lt;/refreshMode&gt;') == -1 or \
       data.find('&lt;refreshInterval&gt;60&lt;/refreshInterval&gt;') == -1 or \
       data.find('&lt;httpQuery&gt;[clientVersion]&lt;/httpQuery&gt;') == -1 or \
       data.find('&lt;viewRefreshMode&gt;onStop&lt;/viewRefreshMode&gt;') == -1 or \
       data.find('&lt;viewRefreshTime&gt;30&lt;/viewRefreshTime&gt;') == -1 or \
       data.find('&lt;refreshMode&gt;onExpire&lt;/refreshMode&gt;') == -1 or \
       data.find('&lt;viewRefreshMode&gt;onRegion&lt;/viewRefreshMode&gt;') == -1 or \
       data.find('&lt;viewBoundScale&gt;0.5&lt;/viewBoundScale&gt;') == -1 or \
       data.find('&lt;viewFormat&gt;BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]&lt;/viewFormat&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing PhotoOverlay

def ogr_libkml_write_photooverlay():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_photooverlay.kml")
    lyr = ds.CreateLayer('test')
    lyr.CreateField(ogr.FieldDefn('name', ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("heading", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("tilt", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("roll", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("camera_longitude", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("camera_latitude", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("camera_altitude", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("camera_altitudemode", ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("photooverlay", ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("leftfov", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("rightfov", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("bottomfov", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("topfov", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("near", ogr.OFTReal))
    lyr.CreateField(ogr.FieldDefn("photooverlay_shape", ogr.OFTString))
    lyr.CreateField(ogr.FieldDefn("imagepyramid_tilesize", ogr.OFTInteger))
    lyr.CreateField(ogr.FieldDefn("imagepyramid_maxwidth", ogr.OFTInteger))
    lyr.CreateField(ogr.FieldDefn("imagepyramid_maxheight", ogr.OFTInteger))

    feat = ogr.Feature(lyr.GetLayerDefn())
    feat.SetField("name", "a photo overlay")
    feat.SetField("photooverlay", "http://www.gdal.org/gdalicon.png")
    feat.SetField("camera_longitude", 2.2946)
    feat.SetField("camera_latitude", 48.8583)
    feat.SetField("camera_altitude", 20)
    feat.SetField("camera_altitudemode", "relativeToGround")
    feat.SetField("leftfov", -60)
    feat.SetField("rightfov", 59)
    feat.SetField("bottomfov", -58)
    feat.SetField("topfov", 57)
    feat.SetField("near", 100)
    feat.SetField("heading", 0)
    feat.SetField("tilt", 90)
    feat.SetField("roll", 0)
    feat.SetField("photooverlay_shape", "rectangle")
    feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2.2945 48.85825)'))
    lyr.CreateFeature(feat)

    feat.SetField("photooverlay", "http://tile.openstreetmap.org/$[level]/$[x]/$[y].png")
    feat.SetField("imagepyramid_tilesize", 256)
    feat.SetField("imagepyramid_maxwidth", 512)
    feat.SetField("imagepyramid_maxheight", 512)
    lyr.CreateFeature(feat)

    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_photooverlay.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;Camera&gt;') == -1 or \
       data.find('&lt;longitude&gt;2.2946&lt;/longitude&gt;') == -1 or \
       data.find('&lt;latitude&gt;48.8583&lt;/latitude&gt;') == -1 or \
       data.find('&lt;altitude&gt;20&lt;/altitude&gt;') == -1 or \
       data.find('&lt;heading&gt;0&lt;/heading&gt;') == -1 or \
       data.find('&lt;tilt&gt;90&lt;/tilt&gt;') == -1 or \
       data.find('&lt;roll&gt;0&lt;/roll&gt;') == -1 or \
       data.find('&lt;altitudeMode&gt;relativeToGround&lt;/altitudeMode&gt;') == -1 or \
       data.find('&lt;href&gt;http://www.gdal.org/gdalicon.png&lt;/href&gt;') == -1 or \
       data.find('&lt;leftFov&gt;-60&lt;/leftFov&gt;') == -1 or \
       data.find('&lt;rightFov&gt;59&lt;/rightFov&gt;') == -1 or \
       data.find('&lt;bottomFov&gt;-58&lt;/bottomFov&gt;') == -1 or \
       data.find('&lt;topFov&gt;57&lt;/topFov&gt;') == -1 or \
       data.find('&lt;near&gt;100&lt;/near&gt;') == -1 or \
       data.find('2.2945,48.85825,0') == -1 or \
       data.find('&lt;shape&gt;rectangle&lt;/shape&gt;') == -1 or \
       data.find('&lt;href&gt;http://tile.openstreetmap.org/$[level]/$[x]/$[y].png&lt;/href&gt;') == -1 or \
       data.find('&lt;tileSize&gt;256&lt;/tileSize&gt;') == -1 or \
       data.find('&lt;maxWidth&gt;512&lt;/maxWidth&gt;') == -1 or \
       data.find('&lt;maxHeight&gt;512&lt;/maxHeight&gt;') == -1 or \
       data.find('&lt;gridOrigin&gt;upperLeft&lt;/gridOrigin&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing and reading Data element

def ogr_libkml_read_write_data():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_read_write_data.kml")
    gdal.SetConfigOption('LIBKML_USE_SIMPLEFIELD', 'NO')
    lyr = ds.CreateLayer('test')
    gdal.SetConfigOption('LIBKML_USE_SIMPLEFIELD', None)
    lyr.CreateField(ogr.FieldDefn('foo', ogr.OFTString))
    feat = ogr.Feature(lyr.GetLayerDefn())
    feat.SetField("foo", "bar")
    feat.SetGeometry(ogr.CreateGeometryFromWkt('POINT(2.2945 48.85825)'))
    lyr.CreateFeature(feat)
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_read_write_data.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;Data name="foo"&gt;') == -1 or \
       data.find('&lt;value&gt;bar&lt;/value&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    ds = ogr.Open("/vsimem/ogr_libkml_read_write_data.kml")
    lyr = ds.GetLayer(0)
    feat = lyr.GetNextFeature()
    if feat.GetField('foo') != 'bar':
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test writing layer as Folder

def ogr_libkml_write_folder():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_folder.kml")
    lyr = ds.CreateLayer('test', options = [ 'LISTSTYLE_ICON_HREF=http://foo', 'FOLDER=YES' ] )
    lyr = ds.CreateLayer('test2', options = [ 'FOLDER=YES' ] )
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_folder.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;Style id="test_liststyle"&gt;') == -1 or \
       data.find('&lt;href&gt;http://foo&lt;/href&gt;') == -1 or \
       data.find('&lt;Folder id="test"&gt;') == -1 or \
       data.find('&lt;styleUrl&gt;#test_liststyle&lt;/styleUrl&gt;') == -1 or \
       data.find('&lt;Folder id="test2"&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'
    
###############################################################################
# Test writing datasource and layer container propreties

def ogr_libkml_write_container_properties():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.GetDriverByName('LIBKML').CreateDataSource("/vsimem/ogr_libkml_write_container_properties.kml",
                                 options = [ 'NAME=ds_name', 'DESCRIPTION=ds_description', 'OPEN=1', 'VISIBILITY=1', 'SNIPPET=ds_snippet'])
    lyr = ds.CreateLayer('test', options = [ 'NAME=lyr_name', 'DESCRIPTION=lyr_description', 'OPEN=0', 'VISIBILITY=0', 'SNIPPET=lyr_snippet'] )
    ds = None

    f = gdal.VSIFOpenL('/vsimem/ogr_libkml_write_container_properties.kml', 'rb')
    data = gdal.VSIFReadL(1, 2048, f)
    data = data.decode('ascii')
    gdal.VSIFCloseL(f)

    if data.find('&lt;name&gt;ds_name&lt;/name&gt;') == -1 or \
       data.find('&lt;visibility&gt;1&lt;/visibility&gt;') == -1 or \
       data.find('&lt;open&gt;1&lt;/open&gt;') == -1 or \
       data.find('&lt;snippet&gt;ds_snippet&lt;/snippet&gt;') == -1 or \
       data.find('&lt;description&gt;ds_description&lt;/description&gt;') == -1 or \
       data.find('&lt;name&gt;lyr_name&lt;/name&gt;') == -1 or \
       data.find('&lt;visibility&gt;0&lt;/visibility&gt;') == -1 or \
       data.find('&lt;open&gt;0&lt;/open&gt;') == -1 or \
       data.find('&lt;snippet&gt;lyr_snippet&lt;/snippet&gt;') == -1 or \
       data.find('&lt;description&gt;lyr_description&lt;/description&gt;') == -1:
        print(data)
        gdaltest.post_reason('failure')
        return 'fail'

    return 'success'

###############################################################################
# Test reading gx:TimeStamp and gx:TimeSpan

def ogr_libkml_read_gx_timestamp():

    if not ogrtest.have_read_libkml:
        return 'skip'

    ds = ogr.Open('data/gxtimestamp.kml')
    lyr = ds.GetLayer(0)
    f = lyr.GetNextFeature()
    if f['timestamp'] != '2016/02/13 12:34:56+00':
        gdaltest.post_reason('failure')
        f.DumpReadable()
        return 'fail'
    f = lyr.GetNextFeature()
    if f['begin'] != '2016/02/13 00:00:00+00' or f['end'] != '2016/02/14 00:00:00+00':
        gdaltest.post_reason('failure')
        f.DumpReadable()
        return 'fail'

    return 'success'

###############################################################################
#  Cleanup

def ogr_libkml_cleanup():
    if not ogrtest.have_read_libkml:
        return 'skip'

    if ogrtest.kml_ds is not None:
        ogrtest.kml_ds.Destroy()

    gdal.Unlink('/vsimem/libkml.kml')
    gdal.Unlink('/vsimem/libkml.kmz')
    gdal.Unlink('/vsimem/libkml_use_doc_off.kmz')
    gdal.Unlink("/vsimem/ogr_libkml_camera.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_layer_lookat.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_layer_camera.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_multigeometry.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_snippet.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_atom_author.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_atom_link.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_phonenumber.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_region.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_screenoverlay.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_model.kml")
    gdal.Unlink("/vsimem/ogr_libkml_read_write_style_read.kml")
    gdal.Unlink("/vsimem/ogr_libkml_read_write_style_write.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_update.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_update.kmz")
    gdal.Unlink("/vsimem/ogr_libkml_write_update_dir/doc.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_update_dir")
    gdal.Unlink("/vsimem/ogr_libkml_write_networklinkcontrol.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_networklinkcontrol.kmz")
    gdal.Unlink("/vsimem/ogr_libkml_write_networklinkcontrol_dir/doc.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_networklinkcontrol_dir")
    gdal.Unlink("/vsimem/ogr_libkml_write_liststyle.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_networklink.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_photooverlay.kml")
    gdal.Unlink("/vsimem/ogr_libkml_read_write_data.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_folder.kml")
    gdal.Unlink("/vsimem/ogr_libkml_write_container_properties.kml")

    # Re-register KML driver if necessary
    if ogrtest.kml_drv is not None:
        print('Re-register KML driver')
        ogrtest.kml_drv.Register()

    return 'success'

###############################################################################
# Build tests runner

gdaltest_list = [ 
    ogr_libkml_datastore,
    ogr_libkml_attributes_1,
    ogr_libkml_attributes_2,
    ogr_libkml_attributes_3,
    ogr_libkml_attributes_4,
    ogr_libkml_point_read,
    ogr_libkml_linestring_read,
    ogr_libkml_polygon_read,
    ogr_libkml_write_kml,
    ogr_libkml_check_write_kml,
    ogr_libkml_write_kmz,
    ogr_libkml_check_write_kmz,
    ogr_libkml_write_kmz_use_doc_off,
    ogr_libkml_check_write_kmz_use_doc_off,
    ogr_libkml_write_dir,
    ogr_libkml_check_write_dir,
    ogr_libkml_xml_attributes,
    ogr_libkml_read_geometries,
    ogr_libkml_test_ogrsf,
    ogr_libkml_read_placemark,
    ogr_libkml_read_empty,
    ogr_libkml_read_emptylayers,
    ogr_libkml_read_schema,
    ogr_libkml_extended_data_without_schema_data,
    ogr_libkml_gxtrack,
    ogr_libkml_camera,
    ogr_libkml_write_layer_lookat,
    ogr_libkml_write_layer_camera,
    ogr_libkml_write_multigeometry,
    ogr_libkml_write_snippet,
    ogr_libkml_write_atom_author,
    ogr_libkml_write_atom_link,
    ogr_libkml_write_phonenumber,
    ogr_libkml_write_region,
    ogr_libkml_write_screenoverlay,
    ogr_libkml_write_model,
    ogr_libkml_read_write_style,
    ogr_libkml_write_update,
    ogr_libkml_write_networklinkcontrol,
    ogr_libkml_write_liststyle,
    ogr_libkml_write_networklink,
    ogr_libkml_write_photooverlay,
    ogr_libkml_read_write_data,
    ogr_libkml_write_folder,
    ogr_libkml_write_container_properties,
    ogr_libkml_read_gx_timestamp,
    ogr_libkml_cleanup ]

if __name__ == '__main__':

    gdaltest.setup_run( 'ogr_libkml' )

    gdaltest.run_tests( gdaltest_list )

    gdaltest.summarize()

</pre></body></html>