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

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

Changed modules to use ET. Added a run-all-tests script (run_tests.sh).

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