source: DPPP/kml/csml2kml/python/csml2kml/PointSeriesConvertor.py @ 3322

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/DPPP/kml/csml2kml/python/csml2kml/PointSeriesConvertor.py@3322
Revision 3322, 6.9 KB checked in by mkochan, 12 years ago (diff)
Line 
1from cElementTree import ElementTree, Element, SubElement, XML
2from pylab import *
3from datetime import datetime, timedelta
4import re
5import csml
6import urllib
7from KMLDocument import *
8from QuadTree import *
9from utils import wget
10
11class Station:
12    '''Represents a ground meteorological station, identified by id, with description, desc, at (lon,lat) '''
13    def __init__(self, id, desc, lon, lat):
14        self.id = id
15        self.desc = desc
16        self.lon = lon; self.lat = lat
17
18    def __repr__(self):
19        return 'Station %s ("%s")' % (self.id, self.desc)
20
21class PointSeriesConvertor:
22    '''
23    Converter from GML+CSML to KML. Contains functionality for converting GML containing a collection of Station elements into KML.
24    '''
25   
26    def __init__(self, config, kmlFilename):
27        self.config = config
28        self.kmlFilename = kmlFilename
29
30    def convert(self):
31        '''
32        Convert GML input from GeoServer (containing the <Station> tags with station information)
33        to the KML representation.
34        [NOTE] At the moment, since the <Station> GML-feature element does not contain a list of CSML features,
35               the output of this code is not entirely correct. In particular, the HTTP reference to the grapher web-service
36               cannot be correctly determined.
37        '''
38
39       def _parseGMLStationsCollection(textGml):
40            '''
41            Parse the GML returned from GeoServer and yield Station objects with info about each station.
42            '''
43            root = XML(textGml)
44            featureCollectionElement = root
45            for featureMember in featureCollectionElement.getchildren():
46                stationElement = featureMember.getchildren()[0]
47                (stationNameElement, stationIDElement, locationElement) = stationElement.getchildren()
48                pointElement = locationElement.getchildren()[0]
49                posElement = pointElement.getchildren()[0]
50                (lon,lat) = map(float, posElement.text.split())
51                yield Station(stationElement.get('{http://www.opengis.net/gml}id'), stationNameElement.text, lon, lat)
52
53        def _buildKmlDocumentDirectly(kmlDocument, stations, placemarkKmlStyle, stationData):
54            '''
55            Returns an object of the KMLDocument class, kmlDocument, which will contain each station
56            represented by a placemark directly contained in the document (i.e. no intermediate folders).
57            '''
58            for station in stations:
59                kmlStation = KMLPlacemark(station.id, station.desc, station.lon, station.lat,
60                                          styleID = placemarkKmlStyle.id, data = stationData
61                                          )
62                kmlDocument.elements.append(kmlStation)
63            return kmlDocument
64
65        def _buildKmlDocumentUsingSections(kmlDocument, stations, placemarkKmlStyle, stationData):
66            '''
67            Returns an object of the KMLDocument class, kmlDocument, which contains the stations
68            organized into a hierarchy of folders, each containing ever smaller section of land.
69            The root of this hierarchy, a KMLFolder object, gets attached to the document,
70            and is "closed" i.e. it is not possible to open it in Google Earth's list view.
71            '''
72
73            def _quadTreeToKMLFolder(t):
74                '''
75                Convert the quad-tree into a KMLElement instance being a folder hierarchy with placemark leaves.
76                '''
77                if isinstance(t, NilLeaf):                      # Do nothing
78                    pass
79                elif isinstance(t, LocLeaf):                    # Create a folder with an embedded region and placemarks
80                    kmlRegion = KMLRegion(t.bbox.west, t.bbox.south, t.bbox.east, t.bbox.north)
81                    kmlFolder = KMLFolder(repr(t.bbox), [], region = kmlRegion)
82                    for location in t.locations:
83                        station = location.obj
84                        kmlPlacemark = KMLPlacemark(
85                            station.desc, station.desc, station.lon, station.lat,
86                            styleID = placemarkKmlStyle.id, data = stationData
87                            )
88                        kmlFolder.children.append(kmlPlacemark)
89                    return kmlFolder
90                elif isinstance(t, Node):                       # Create a folder containing nested elements
91                    kmlRegion = KMLRegion(t.bbox.west, t.bbox.south, t.bbox.east, t.bbox.north)
92                    kmlFolder = KMLFolder(repr(t.bbox), [], region = kmlRegion)
93                    for i in t.children:
94                        kmlChild = _quadTreeToKMLFolder(i)
95                        if kmlChild:
96                            kmlFolder.children.append(kmlChild)
97                    return kmlFolder
98           
99            locations = []
100            for station in stations:
101                locations.append( Location(station.lon, station.lat, station) )
102            quadTree = build_quadtree(locations)
103            kmlFolder = _quadTreeToKMLFolder(quadTree)
104
105            lockedFolderStyle = KMLStyle('locked_folder_style', listItemType = 'checkHideChildren')
106            kmlFolder.styleID = lockedFolderStyle.id
107            kmlDocument.styles.append(lockedFolderStyle)
108
109            kmlDocument.elements.append(kmlFolder)
110            return kmlDocument
111
112        #-----------------------------------------------------------------------------
113
114        # Read conversion configuration
115        documentName = self.config.find('name').text
116        url = self.config.find('GeoServerRequest/URL').text
117        balloonTemplate = self.config.find('GeoServerRequest/BalloonTemplate').text
118        stationData = {}
119        data = self.config.findall('GeoServerRequest/StationData/Datum')
120        for datum in data:
121            datumName = datum.get('name')
122            datumValue = datum.text
123            stationData[datumName] = datumValue
124        useSections = self.config.find('UseRegions').text == 'yes'
125
126        # Read in GML containing the stations, and extract them into a list of Station objects
127        textGml = wget(url)
128        stations = _parseGMLStationsCollection(textGml)
129
130        # Create a KML style for each station/placemark -- can contain appropriate HTML balloon template
131        placemarkKmlStyle = createDefaultPlacemarKMLStyle(balloonTemplate = balloonTemplate)
132
133        # Create a KML document and attach the stations either directly to it, or using a folder hierarchy
134        # of KML sections (derived by means of quad-tree)
135        kmlDocument = KMLDocument(documentName, [placemarkKmlStyle])
136        if useSections:
137            kmlDocument = _buildKmlDocumentUsingSections(kmlDocument, stations, placemarkKmlStyle, stationData)
138        else:
139            kmlDocument = _buildKmlDocumentDirectly(kmlDocument, stations, placemarkKmlStyle, stationData)
140
141        # Save the KML document
142        kmlDocument.save(self.kmlFilename)
Note: See TracBrowser for help on using the repository browser.