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

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

Extended StationConvertor? to handle colouring of stations. Added config file for the ECN dataset.

Line 
1'''
2Contains class C{StationConvertor}.
3'''
4
5from ET import ElementTree, Element, SubElement, XML
6import re
7from KML import *
8from QuadTree import *
9import utils
10from Station import NPStation, WFSStationCollection
11
12class StationConvertor:
13    '''
14    Contains functionality for converting a collection of <np:Station>'s into a KML or a KMZ file.
15    '''
16   
17    def __init__(self, config):
18        '''
19        Initialize the convertor and set it up according to the configuration.
20        @param config: An element containing contents of a <NPStations2KML> element from a XML config file.
21        @type config: C{cElementTree.Element}
22        '''
23       
24        self.config = config
25
26        # Read the KMZ file name
27        self.kmlFilename = self.config.find('OutputKmzFilename').text
28
29        # Read the document name
30        self.documentName = self.config.find('DocumentName').text
31
32        # Read the dataset identifier
33        ##### self.datasetId = self.config.find('DatasetID').text
34
35        # URL of GeoServer to get a <wfs:StationCollection> from
36        self.geoServerRequestUrl = self.config.find('GetAllStationsRequestURL').text
37
38        # Get template for placemark balloons; create two KML styles for white and red placemarks
39        # (red for stations with embedded features, white for the rest).
40        self.balloonTemplate = self.config.find('BalloonTemplate').text
41        self.whitePlacemarkKmlStyle = createDefaultPlacemarKMLStyle(balloonTemplate = self.balloonTemplate)
42        self.whitePlacemarkKmlStyle.id = 'white_placemark_style'
43        self.redPlacemarkKmlStyle = createDefaultPlacemarKMLStyle(balloonTemplate = self.balloonTemplate)
44        self.redPlacemarkKmlStyle.id = 'red_placemark_style'
45        self.redPlacemarkKmlStyle.iconColor = 'ff4444ff' # light red (white is default so no need to set it)
46
47        # Initialize placemark data -- i.e. values to be substituted for placeholders in the balloon template
48        self.stationData = {}
49        data = self.config.findall('StationData/Datum')
50        for datum in data:
51            datumName = datum.get('name')
52            datumValue = datum.text
53            self.stationData[datumName] = datumValue
54
55        # Should we use KML regions? (recommended for high number of stations)
56        self.useSections = self.config.find('UseRegions').text == 'yes'
57
58        # Create a KML style to be used for holding a KML folder hierarchy in a folder that can't open
59        self.lockedFolderStyle = KMLStyle('locked_folder_style', listItemType = 'checkHideChildren')
60
61        # Initialize the viewing interval
62        self.displayIntervalStart = utils.parseTimestamp(self.config.find('DisplayIntervalStart').text)
63        self.displayIntervalEnd = utils.parseTimestamp(self.config.find('DisplayIntervalEnd').text)
64
65    def _npStationToKmlPlacemark(self, npStation):
66        '''
67        Converts a C{Station.NPStation} object into it's C{KML.KMLPlacemark} representation.
68        Uses red placemark style for stations with some embedded features, white style for the remaining stations.
69        @rtype: C{KML.KMLPlacemark}
70        '''
71        # isVisibleFeature will be True iff the stations contains AT LEAST ONE feature that measurement time in that period
72        isVisibleFeature = False
73        for stationFeature in npStation.stationFeatures:
74            if self.displayIntervalStart >= stationFeature.collectBeginDate and self.displayIntervalEnd <= stationFeature.collectEndDate:
75                isVisibleFeature = True
76                break
77
78        if isVisibleFeature:
79            placemarkKmlStyle = self.redPlacemarkKmlStyle
80        else:
81            placemarkKmlStyle = self.whitePlacemarkKmlStyle
82        return KMLPlacemark(npStation.id, npStation.desc, npStation.lon, npStation.lat,
83                            styleID = placemarkKmlStyle.id, data = self.stationData
84                            )
85
86    def _buildKmlDocumentDirectly(self, kmlDocument, npStations):
87        '''
88        Returns a C{KMLDocument} object with stations represented by KML placemarks directly contained
89        in the document (i.e. without any intermediate folders).
90        @param kmlDocument: A document to which the folder hierarchy should be attached.
91        @type kmlDocument: C{KML.KMLDocument}
92        @param npStations: A list of stations to be contained in the hierarchy.
93        @type npStations: List of C{Station.NPStation} objects.
94        @rtype: C{KML.KMLDocument}
95        '''
96        for npStation in npStations:
97            kmlDocument.elements.append( self._npStationToKmlPlacemark(npStation) )
98        return kmlDocument
99
100    def _buildKmlDocumentUsingSections(self, kmlDocument, npStations):
101        '''
102        Returns a C{KMLDocument} object containing stations as KML placemarks wrapped in a hierarchy
103        of C{KMLFolder}'s with associated C{KMLRegion}'s. The regions are recursively decreasing in size.
104        @param kmlDocument: A document to which the folder hierarchy should be attached.
105        @type kmlDocument: C{KML.KMLDocument}
106        @param npStations: A list of stations to be contained in the hierarchy.
107        @type npStations: List of C{Station.NPStation} objects.
108        @rtype: C{KML.KMLDocument}
109        '''
110
111        def _quadTreeToKMLFolder(t):
112            '''
113            Convert the quad-tree into a KMLElement instance being a folder hierarchy with placemark leaves.
114            '''
115            if isinstance(t, NilLeaf):                      # Do nothing
116                pass
117            elif isinstance(t, LocLeaf):                    # Create a folder with an embedded region and placemarks
118                kmlRegion = KMLRegion(t.bbox.west, t.bbox.south, t.bbox.east, t.bbox.north)
119                kmlFolder = KMLFolder(repr(t.bbox), [], region = kmlRegion)
120                for location in t.locations:
121                    npStation = location.obj
122                    kmlPlacemark = self._npStationToKmlPlacemark(npStation)
123                    kmlFolder.children.append(kmlPlacemark)
124                return kmlFolder
125            elif isinstance(t, Node):                       # Create a folder containing nested elements
126                kmlRegion = KMLRegion(t.bbox.west, t.bbox.south, t.bbox.east, t.bbox.north)
127                kmlFolder = KMLFolder(repr(t.bbox), [], region = kmlRegion)
128                for i in t.children:
129                    kmlChild = _quadTreeToKMLFolder(i)
130                    if kmlChild:
131                        kmlFolder.children.append(kmlChild)
132                return kmlFolder
133
134        # Create a list of locations (that is, of QuadTree.Location objects), associated with the stations;
135        # each location will also hold the npStation object it refers to.
136        locations = []
137        for npStation in npStations:
138            locations.append( Location(npStation.lon, npStation.lat, npStation) )
139
140        # Build a quad-tree from the locations, and convert it into a KMLFolder object.
141        quadTree = build_quadtree(locations)
142        kmlStationsFolder = KMLFolder('Stations', [_quadTreeToKMLFolder(quadTree)], opened = False)
143
144        # Add kmlStationsFolder to the current document
145        kmlDocument.elements.append(kmlStationsFolder)
146
147        # Make the folder follow the "locked folder" style
148        kmlDocument.styles.append(self.lockedFolderStyle)
149        kmlStationsFolder.styleID = self.lockedFolderStyle.id
150
151        return kmlDocument
152   
153    def convert(self):
154        '''
155        Perform the conversion.
156        This will produce a KML or KMZ file, depending on the suffix of the output file as defined in the config file.
157
158        The stations will be organised depending on the C{UseRegions} setting in the config file:
159          - if C{UseRegions} is set to C{no},
160            stations will be included simply as a list of KML placemarks.
161          - if C{UseRegions} is set to C{yes},
162            stations will be divided into a hierarchy of KML regions, by recursively splitting
163            the the covered are into 4 smaller regions, starting with the whole globe.
164            The actual station KML placemarks will be placed at the bottom of the hierarchy.
165            Therefore, they will be visible only for closer zooms onto the ground.
166        '''
167
168        # Get a collection of stations from a GeoServer, and put the stations into a list
169        geoServerResponse = utils.wget(self.geoServerRequestUrl)
170        wfsStationsCollection = WFSStationCollection()
171        wfsStationsCollection.parseString(geoServerResponse)
172        npStations = wfsStationsCollection.stations
173
174        # Create a KML document and attach the stations, either directly or using KML regions
175        kmlDocument = KMLDocument(self.documentName, [self.whitePlacemarkKmlStyle, self.redPlacemarkKmlStyle])
176        if self.useSections:
177            kmlDocument = self._buildKmlDocumentUsingSections(kmlDocument, npStations)
178        else:
179            kmlDocument = self._buildKmlDocumentDirectly(kmlDocument, npStations)
180
181        # Save the KML document
182        kmlDocument.save(self.kmlFilename)
Note: See TracBrowser for help on using the repository browser.