Changeset 3260 for DPPP/kml


Ignore:
Timestamp:
23/01/08 16:36:51 (12 years ago)
Author:
mkochan
Message:

Changed quadrize_stations.py to parse in the MIDAS stations data in "ukmo.kml". Added comments and did a slight polishing of code.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • DPPP/kml/csml2kml/python/prototypes/quadrize_stations.py

    r3259 r3260  
     1''' 
     2Prototype for producing KML documents containing a quad-tree like <Folder> structure containing locations 
     3in an effective way. See description of the build_quadtree() function to see what a "quad-tree" is. 
     4''' 
     5 
    16from random import random 
    27from cElementTree import ElementTree, Element, SubElement 
    38 
     9class Location: 
     10    ''' 
     11    Represents a (station) location with a name 
     12    ''' 
     13    def __init__(self, name, lon, lat, alt): 
     14        self.name = name 
     15        self.lon = lon; self.lat = lat; self.alt = alt 
     16    def __repr__(self): 
     17        return '"%s% @ (%f,%f,%f)' % (self.name, self.lon, self.lat, self.alt) 
     18 
    419class Tree: 
     20    ''' 
     21    Abstract class, represents a tree of regions/locations. The tree can be converted 
     22    directly to a KML document which contains station locations using KML regions. 
     23    ''' 
    524    pass 
    625 
     
    928 
    1029class NilLeaf(Leaf): 
     30    ''' 
     31    Leaf which contains nothing in itself. Used whenever an empty region has to be created 
     32    (i.e. a region with no stations in it). This is because a Node should always contain 4 children. 
     33    ''' 
    1134    def __repr__(self): 
    1235        return 'nil' 
    1336 
    1437class LocLeaf(Leaf): 
     38    ''' 
     39    A tree leaf containing locations, i.e. Location objects. 
     40    ''' 
    1541    def __init__(self, locations, bbox): 
    1642        self.bbox = bbox 
     
    2450 
    2551class Node(Tree): 
     52    ''' 
     53    A tree node which contains children, being themselves Tree objects. 
     54    ''' 
    2655    def __init__(self, children, bbox): 
    2756        self.bbox = bbox 
    28         if not children: 
    29             raise ValueError('Empty child list passed') 
    30         if len(children) > 4: 
    31             raise ValueError('More than 4 children passed') 
     57        #if not children: 
     58        #    raise ValueError('Empty child list passed') 
     59        if len(children) != 4: 
     60            raise ValueError('Exactly 4 tree children must be passed') 
    3261        self.children = children 
    3362    def __repr__(self): 
     
    3665        return '[' + s + '] in ' + repr(self.bbox) 
    3766 
    38 # Must use the lon in (-180,180), lat in (-90,90) bounds 
    3967class BBox: 
     68    ''' 
     69    Represents a bounding box, i.e. a geographical 2D region (considering longitute & latitude only, 
     70    ignoring altitude). Contains 4 methods for splitting the region into its SW, SE, NW, NE parts 
     71    (i.e. quadratic splitting). 
     72    ''' 
     73 
    4074    def __init__(self, west, south, east, north): 
     75        if west < -180. or south < -90. or east > 180 or north > 90: 
     76            raise ValueError('Degree(s) out of bounds') 
    4177        self.west = west; self.east = east 
    4278        self.south = south; self.north = north 
     
    5692        return BBox(self.lon_centre(), self.lat_centre(), self.east, self.north) 
    5793 
    58 def f(l,bbox): 
     94#--------------------------------------------------------------------------------------------------------------------------- 
     95 
     96def build_quadtree(l,bbox,max_per_region=16): 
     97    ''' 
     98    Description:               Performs quad-tree segmentation of space, to store (station) locations 
     99                               in a tree that contains locations in a hierarchy of smaller and smaller segments/nodes, 
     100                               each smaller node produced by splitting the space into 4 sub-spaces. 
     101     
     102    Input:               l ... List of (station) locations, i.e. of Location objects 
     103                      bbox ... A BBox object determining the main bounding box enwrapping 
     104                               all possible time boxes, i.e. the one from which to start dividing. 
     105                               Supply BBox(-180.,-90.,180.,90.) to use whole globe. 
     106            max_per_region ... Maximum number of locations to be contained in a lowest-level region 
     107                               (i.e. in a LocLeaf object). This determines the depth of the resultant 
     108                               tree. 
     109 
     110    Output:                    The quad-tree. 
     111    ''' 
    59112    if len(l) == 0: 
    60113        return NilLeaf() 
     
    64117        (l1, l2, l3, l4) = ([], [], [], []) 
    65118        for i in l: 
    66             (lon,lat) = i 
     119            (lon,lat) = (i.lon,i.lat) 
    67120            if lat < bbox.lat_centre(): 
    68121                if lon < bbox.lon_centre(): 
     
    75128                else: 
    76129                    l4.append(i) # NE 
    77         t1 = f(l1, bbox.sw()) 
    78         t2 = f(l2, bbox.se()) 
    79         t3 = f(l3, bbox.nw()) 
    80         t4 = f(l4, bbox.ne()) 
     130        t1 = build_quadtree(l1, bbox.sw()) 
     131        t2 = build_quadtree(l2, bbox.se()) 
     132        t3 = build_quadtree(l3, bbox.nw()) 
     133        t4 = build_quadtree(l4, bbox.ne()) 
    81134        return Node([t1, t2, t3, t4],bbox) 
    82135 
    83 def g(t): 
     136#--------------------------------------------------------------------------------------------------------------------------- 
     137 
     138def kml_from_quadtree(t): 
     139    ''' 
     140    Convert the quad-tree into a KML element containing a <Folder> hierarchy with <Placemark> leaves. 
     141    ''' 
     142    def _buildRegionElement(bbox): 
     143        ''' 
     144        Convert a BBox object to a KML <Region> element 
     145        ''' 
     146        llabElement = Element('LatLonAltBox') 
     147        SubElement(llabElement, 'west').text = str(bbox.west) 
     148        SubElement(llabElement, 'south').text = str(bbox.south) 
     149        SubElement(llabElement, 'east').text = str(bbox.east) 
     150        SubElement(llabElement, 'north').text = str(bbox.north) 
     151 
     152        lodElement = Element('Lod') 
     153        SubElement(lodElement, 'minLodPixels').text = '64' 
     154        SubElement(lodElement, 'maxLodPixels').text = '-1' 
     155        SubElement(lodElement, 'minFadeExtent').text = '128' 
     156 
     157        regionElement = Element('Region') 
     158        regionElement.append(llabElement) 
     159        regionElement.append(lodElement) 
     160        return regionElement 
     161 
     162    def _buildPlacemarkElement(loc): 
     163        ''' 
     164        Convert a Location object to a KML <Placemark> element 
     165        ''' 
     166        pointElement = Element('Point') 
     167        SubElement(pointElement, 'coordinates').text = '%f,%f,%f' % (loc.lon,loc.lat,loc.alt) 
     168 
     169        placemarkElement = Element('Placemark') 
     170        SubElement(placemarkElement, 'name').text = loc.name 
     171        placemarkElement.append(pointElement) 
     172        return placemarkElement 
     173 
    84174    if isinstance(t, NilLeaf):                      # Do nothing 
    85175        pass 
     
    87177        folderElement = Element('Folder') 
    88178        SubElement(folderElement, 'name').text = repr(t.bbox) 
    89         folderElement.append(buildRegionElement(t.bbox)) 
     179        folderElement.append(_buildRegionElement(t.bbox)) 
    90180        for loc in t.locations: 
    91             folderElement.append(buildPlacemarkElement(loc)) 
     181            folderElement.append(_buildPlacemarkElement(loc)) 
    92182        return folderElement 
    93183    elif isinstance(t, Node):                       # Create a <Folder> containing nested elements 
    94184        folderElement = Element('Folder') 
    95185        SubElement(folderElement, 'name').text = repr(t.bbox) 
    96         folderElement.append(buildRegionElement(t.bbox)) 
     186        folderElement.append(_buildRegionElement(t.bbox)) 
    97187        for i in t.children: 
    98             childElement = g(i)  # could be None 
     188            childElement = kml_from_quadtree(i)  # could be None 
    99189            if childElement: 
    100190                folderElement.append(childElement) 
    101191        return folderElement 
    102192 
    103 def buildRegionElement(bbox): 
    104  
    105     llabElement = Element('LatLonAltBox') 
    106     SubElement(llabElement, 'west').text = str(bbox.west) 
    107     SubElement(llabElement, 'south').text = str(bbox.south) 
    108     SubElement(llabElement, 'east').text = str(bbox.east) 
    109     SubElement(llabElement, 'north').text = str(bbox.north) 
    110  
    111     lodElement = Element('Lod') 
    112     SubElement(lodElement, 'minLodPixels').text = '64' 
    113     SubElement(lodElement, 'maxLodPixels').text = '-1' 
    114     SubElement(lodElement, 'minFadeExtent').text = '128' 
    115  
    116     regionElement = Element('Region') 
    117     regionElement.append(llabElement) 
    118     regionElement.append(lodElement) 
    119     return regionElement 
    120  
    121 def buildPlacemarkElement(loc): 
    122  
    123     pointElement = Element('Point') 
    124     (lon,lat) = loc 
    125     SubElement(pointElement, 'coordinates').text = '%f,%f,%f' % (lon,lat,0) 
    126  
    127     placemarkElement = Element('Placemark') 
    128     SubElement(placemarkElement, 'name').text = repr(loc) 
    129     placemarkElement.append(pointElement) 
    130     return placemarkElement 
    131  
    132 def buildCheckHideChildrenStyleElement(): 
    133     listStyleElement = Element('ListStyle') 
    134     SubElement(listStyleElement, 'listItemType').text = 'checkHideChildren' 
    135     styleElement = Element('Style') 
    136     styleElement.set('id', 'checkHideChildrenStyle') 
    137     styleElement.append(listStyleElement) 
    138     return styleElement 
     193#--------------------------------------------------------------------------------------------------------------------------- 
    139194 
    140195def wrapRootFolderElement(rootFolderElement): 
    141  
    142     # Auxiliary function, indents XML 
    143     def indentXML(elem, level=0): 
     196    ''' 
     197    Create a full valid KML document containing the <Folder> element, rootFolderElement. 
     198    ''' 
     199    def _indentXML(elem, level=0):                     # Auxiliary function, indents XML 
    144200        i = "\n" + level * "  " 
    145201        if len(elem): 
     
    147203                elem.text = i + "  " 
    148204            for child in elem: 
    149                 indentXML(child, level+1) 
     205                _indentXML(child, level+1) 
    150206            if not child.tail or not child.tail.strip(): 
    151207                child.tail = i 
     
    155211            if level and (not elem.tail or not elem.tail.strip()): 
    156212                elem.tail = i 
     213 
     214    # Auxiliary func, creates a list style that forces a <Folder> not to be "openable" in the Google Earth list view 
     215    def _buildCheckHideChildrenStyleElement():         
     216        listStyleElement = Element('ListStyle') 
     217        SubElement(listStyleElement, 'listItemType').text = 'checkHideChildren' 
     218        styleElement = Element('Style') 
     219        styleElement.set('id', 'checkHideChildrenStyle') 
     220        styleElement.append(listStyleElement) 
     221        return styleElement 
    157222     
    158223    SubElement(rootFolderElement, 'styleUrl').text = '#checkHideChildrenStyle' 
     
    161226    SubElement(kmlDocumentElement, 'name').text='Example quadrization run' 
    162227    SubElement(kmlDocumentElement, 'open').text='1' 
    163     kmlDocumentElement.append(buildCheckHideChildrenStyleElement()) 
     228    kmlDocumentElement.append(_buildCheckHideChildrenStyleElement()) 
    164229    kmlDocumentElement.append(rootFolderElement) 
    165230 
    166231    kmlRootElement=Element('kml', xmlns='http://earth.google.com/kml/2.2') 
    167232    kmlRootElement.append(kmlDocumentElement) 
    168     indentXML(kmlRootElement, 0) 
     233    _indentXML(kmlRootElement, 0) 
    169234    return kmlRootElement 
    170235 
    171 bbox = BBox(0.,0.,100.,100.) 
    172 max_per_region = 10 
    173 l = [] 
    174 for i in range(10000): 
    175     location = (100. * random(), 100. * random()) 
    176     l.append(location) 
    177 t = f(l, bbox) 
    178 rootFolderElement = g(t) 
     236#--------------------------------------------------------------------------------------------------------------------------- 
     237 
     238def makeRandomStationsQuadTree(): 
     239    ''' 
     240    Debugging-purpose generator of a tree with random locations 
     241    ''' 
     242    bbox = BBox(0.,0.,90.,90.) 
     243    l = [] 
     244    for i in range(10000): 
     245        location = Location('Station_' + str(i), 90. * random(), 90. * random(), 0.) 
     246        l.append(location) 
     247    return build_quadtree(l, bbox) 
     248 
     249def makeUKMOStationsQuadTree(): 
     250    ''' 
     251    Parse the "ukmo.kml" file made by A.S.Harwood@rl.ac.uk (the MIDAS list of UK weather stations) 
     252    ''' 
     253    tree = ElementTree() 
     254    tree.parse('../../testdata/ukmo.kml') 
     255    root = tree.getroot() 
     256 
     257    bbox = BBox(-180.,-90.,180.,90.) 
     258 
     259    def fullname(str): 
     260        ns = 'http://earth.google.com/kml/2.0' 
     261        return '{' + ns + '}' + str 
     262 
     263    counties = root.findall(fullname('Folder') + '/' + fullname('Folder') + '/' + fullname('Folder')) 
     264    l = [] 
     265    for county in counties: 
     266        placemarks = county.findall(fullname('Placemark')) 
     267        for p in placemarks: 
     268            desc_str = p.find(fullname('description')).text 
     269            coords_str = p.find(fullname('Point') + '/' + fullname('coordinates')).text 
     270            (lon,lat,alt) = map(float, coords_str.split(',')) 
     271            l.append(Location(desc_str,lon,lat,alt)) 
     272 
     273    return build_quadtree(l, bbox) 
     274 
     275#--------------------------------------------------------------------------------------------------------------------------- 
     276 
     277# Convert the MIDAS list of stations to a KML document 
     278quadtree = makeUKMOStationsQuadTree() 
     279rootFolderElement = kml_from_quadtree(quadtree) 
    179280kmlRootElement = wrapRootFolderElement(rootFolderElement) 
    180281 
    181282# Write the KML document to a file 
    182283kmlTree = ElementTree(kmlRootElement) 
    183 kmlFile = open('../../output/quadrize.kml', 'w') 
     284kmlFile = open('../../output/quadrize_ukmo.kml', 'w') 
    184285kmlTree.write(kmlFile) 
    185286kmlFile.close() 
Note: See TracChangeset for help on using the changeset viewer.