source: DPPP/kml/csml2kml/python/csml2kml/csml2kml/StationConvertor.py @ 3439

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/DPPP/kml/csml2kml/python/csml2kml/csml2kml/StationConvertor.py@3549
Revision 3439, 6.6 KB checked in by mkochan, 12 years ago (diff)

Prepared file hierarchy for creating an egg "csml2kml"

Line 
1from cElementTree import ElementTree, Element, SubElement, XML
2from pylab import *
3from datetime import datetime, timedelta
4import re
5import csml
6import urllib
7from KML import *
8from QuadTree import *
9from utils import wget
10from Station import NPStation, WFSStationCollection
11
12class StationConvertor:
13    '''
14    Converter from GML+CSML to KML.
15    Contains functionality for converting GML containing a collection of Station elements into KML.
16    '''
17   
18    def __init__(self, config, kmlFilename):
19        '''Initialize the convertor and set it up according to the config file.'''
20       
21        self.config = config
22        self.kmlFilename = kmlFilename
23
24        # Read config document name
25        self.documentName = self.config.find('name').text
26
27        # URL of GeoServer to get a <wfs:StationCollection> from
28        self.geoServerUrl = self.config.find('GeoServerRequest/URL').text
29
30        # Get template for placemark balloons; create a KML style for placemarks
31        self.balloonTemplate = self.config.find('GeoServerRequest/BalloonTemplate').text
32        self.placemarkKmlStyle = createDefaultPlacemarKMLStyle(balloonTemplate = self.balloonTemplate)
33
34        # Initialize placemark data -- i.e. values to be substituted for placeholders in the balloon template
35        self.stationData = {}
36        data = self.config.findall('GeoServerRequest/StationData/Datum')
37        for datum in data:
38            datumName = datum.get('name')
39            datumValue = datum.text
40            self.stationData[datumName] = datumValue
41
42        # Should we use KML regions? (recommended for high number of stations)
43        self.useSections = self.config.find('UseRegions').text == 'yes'
44
45        # Create a KML style to be used for holding a KML folder hierarchy in a folder that can't open
46        self.lockedFolderStyle = KMLStyle('locked_folder_style', listItemType = 'checkHideChildren')
47
48        self.grapherUrl = self.config.find('GrapherRequest/URL').text
49
50    def _npStationToKmlPlacemark(self, npStation):
51        '''Converts a Station.NPStation object into it's KML.KMLPlacemark representation'''
52        featuresHtml = ''
53        for featureId in npStation.csmlFeatureIds:
54            featuresHtml = featuresHtml + '<img src="' + self.grapherUrl + '?feature_id=' + featureId + '"><br>'
55        return KMLPlacemark(npStation.id, npStation.desc, npStation.lon, npStation.lat, featuresHtml,
56                            styleID = self.placemarkKmlStyle.id, data = self.stationData
57                            )
58
59    def _buildKmlDocumentDirectly(self, kmlDocument, npStations):
60        '''
61        Returns an object of the KMLDocument class, kmlDocument, which will contain each station
62        represented by a placemark directly contained in the document (i.e. no intermediate folders).
63        '''
64        for npStation in npStations:
65            kmlDocument.elements.append( self._npStationToKmlPlacemark(npStation) )
66        return kmlDocument
67
68    def _buildKmlDocumentUsingSections(self, kmlDocument, npStations):
69        '''
70        Returns an object of the KMLDocument class, kmlDocument, which contains the stations
71        organized into a hierarchy of folders, each containing ever smaller section of land.
72        The root of this hierarchy, a KMLFolder object, gets attached to the document,
73        and is "closed" i.e. it is not possible to open it in Google Earth's list view.
74        '''
75
76        def _quadTreeToKMLFolder(t):
77            '''
78            Convert the quad-tree into a KMLElement instance being a folder hierarchy with placemark leaves.
79            '''
80            if isinstance(t, NilLeaf):                      # Do nothing
81                pass
82            elif isinstance(t, LocLeaf):                    # Create a folder with an embedded region and placemarks
83                kmlRegion = KMLRegion(t.bbox.west, t.bbox.south, t.bbox.east, t.bbox.north)
84                kmlFolder = KMLFolder(repr(t.bbox), [], region = kmlRegion)
85                for location in t.locations:
86                    npStation = location.obj
87                    kmlPlacemark = self._npStationToKmlPlacemark(npStation)
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        # Create a list of locations (that is, of QuadTree.Location objects), associated with the stations;
100        # each location will also hold the npStation object it refers to.
101        locations = []
102        for npStation in npStations:
103            locations.append( Location(npStation.lon, npStation.lat, npStation) )
104
105        # Build a quad-tree from the locations, and convert it into a KMLFolder object.
106        quadTree = build_quadtree(locations)
107        kmlFolder = _quadTreeToKMLFolder(quadTree)
108
109        # Add kmlFolder to the current document
110        kmlDocument.elements.append(kmlFolder)
111
112        # Make the folder follow the "locked folder" style
113        kmlDocument.styles.append(self.lockedFolderStyle)
114        kmlFolder.styleID = self.lockedFolderStyle.id
115
116        return kmlDocument
117
118    def convert(self):
119        '''
120        Convert GML input from GeoServer (containing the <Station> tags with station information)
121        to the KML representation.
122        [NOTE] At the moment, since the <Station> GML-feature element does not contain a list of CSML features,
123               the output of this code is not entirely correct. In particular, the HTTP reference to the grapher web-service
124               cannot be correctly determined.
125        '''
126
127        # Get a collection of stations from a GeoServer, and put the stations into a list
128        geoServerResponse = wget(self.geoServerUrl)
129        wfsStationsCollection = WFSStationCollection()
130        wfsStationsCollection.parseString(geoServerResponse)
131        npStations = wfsStationsCollection.stations
132
133        # Create a KML document and attach the stations, either directly or using KML regions
134        kmlDocument = KMLDocument(self.documentName, [self.placemarkKmlStyle])
135        if self.useSections:
136            kmlDocument = self._buildKmlDocumentUsingSections(kmlDocument, npStations)
137        else:
138            kmlDocument = self._buildKmlDocumentDirectly(kmlDocument, npStations)
139
140        # Save the KML document
141        kmlDocument.save(self.kmlFilename)
Note: See TracBrowser for help on using the repository browser.