source: subversion/applications/utils/export/tiledata2/initialParse.py @ 28183

Last change on this file since 28183 was 9919, checked in by ojw, 11 years ago

different method

  • Property svn:executable set to *
File size: 8.3 KB
Line 
1#!/usr/bin/python
2# -*- coding: UTF-8 -*-
3import bsddb
4import sys
5import os
6from xml.sax import make_parser, handler
7import xml
8import tilenames
9import enums
10import struct
11
12z = 14
13
14class average:
15  def __init__(self):
16    self.count = 0
17    self.total = 0
18    self.max = 0
19  def add(self,num):
20    self.count += 1
21    self.total += num
22    if(num > self.max):
23      self.max = num
24  def average(self):
25    return(self.total/self.count)
26
27class filePool:
28  def __init__(self, maxfiles = 120):
29    """Keep a pool of filehandles, reusing them and closing the
30    ones which haven't been used for longest.
31    Higher maxfiles mean it doesn't need to open files so often,
32    so if the OS can afford all that many filehandles then great."""
33    self.f = {}
34    self.access = {}
35    self.t = 0
36    self.limit = maxfiles
37    self.numOpens = 0
38    self.average = average()
39  def fp(self,filename, mode="a"):
40    # record access-time
41    self.access[filename] = self.t
42    self.t += 1
43    # if already open, return
44    f = self.f.get(filename, None)
45    if(f):
46      return(f)
47    #print "Opening %s" % filename
48    # if too many open, close one
49    if(len(self.f.keys()) >= self.limit):
50      self.closeOne()
51    # open new file
52    f = open(filename, mode)
53    self.f[filename] = f
54    self.numOpens += 1
55    return(f)
56  def closeOne(self):
57    minT = None
58    oldestFilename = None
59    for filename,t in self.access.items():
60      if(minT == None or t < minT):
61        oldestFilename = filename
62        minT = t
63    #print "T = %d, oldest = %d" % (self.t, minT)
64    if(oldestFilename):
65      self.close(oldestFilename)
66  def close(self,filename):
67    age = self.t - self.access[filename]
68    self.average.add(age)
69    #print "Closing %s, age %d" % (filename, age)
70    self.f[filename].close()
71    del(self.f[filename])
72    del(self.access[filename])
73  def closeAll(self):
74    for k,v in self.f.items():
75      self.close(k)
76    print "Opened %d files, average life %d" % (self.numOpens, self.average.average())
77     
78class dbStore:
79  def __init__(self, filename, nodesExist = False):
80    self.db = bsddb.btopen(filename, 'c')
81    self.nodesExist = nodesExist
82    if(nodesExist):
83      print "Assuming that nodes already exist in %s" % filename
84    self.countNodes = 0
85    self.countWays = 0
86    self.countPoi = 0
87    self.nodeErrors = 0
88    self.promptFreq = 10000
89    self.prompt = self.promptFreq
90    self.setupEnums()
91    self.average = average()
92    self.files = filePool()
93  def cleanup(self):
94    self.files.closeAll()
95  def setupEnums(self):
96    self.enums = enums.makeEnums()
97    self.enumIDs = {}
98    count = 1
99    f = open('enums.txt','w')
100    for name in sorted(self.enums.enums.keys()):
101      f.write("%d\t%s\n" % (count,name))
102      self.enumIDs[name] = count
103      count += 1
104    f.close()
105   
106  def wayStyle(self, tags):
107    styles = []
108    for style in self.enums.equiv:
109      if(style['fn'](tags)):
110        style2 = self.enums.enums[style['equiv']]
111        styles.append(style2)
112    for name, style in self.enums.enums.items():
113      if(style['fn'](tags)):
114        styles.append(style)
115    return(styles)
116           
117  def storeNode(self,id,lat,lon):
118    """Store a node"""
119    if(not self.nodesExist):
120      (x,y) = tilenames.tileXY(lat,lon,z)
121      self.db[str(id)] = "%f,%f,%d_%d"%(lat,lon,x,y)
122     
123      self.countNodes += 1
124      self.prompt -= 1
125      if(not self.prompt):
126        self.prompt = self.promptFreq
127        print "%1.3fM nodes" % (float(self.countNodes) / 1000000.0)
128
129  def storeWay(self,wid,nodes, tags):
130    # Progress/status
131    if(not self.countWays):
132      print "Starting ways"
133      self.prompt = self.promptFreq
134    self.countWays += 1
135    self.prompt -= 1
136    if(not self.prompt):
137      self.prompt = self.promptFreq
138      print "%1.3fM ways" % (float(self.countWays) / 1000000.0)
139   
140    # Lookup the nodes used in the way, find their lat/long,
141    # and compile a list of tiles that the way 'touches'
142    waynodes = []
143    tilesTouched = {}
144    for nid in nodes:
145      try:
146        text = self.db[str(nid)]
147        if(not text):
148          self.nodeErrors += 1
149        else:
150          (lat,lon,tile) = text.split(",")
151          tilesTouched[tile] = True
152          waynodes.append((nid,float(lat),float(lon)))
153      except KeyError:
154        self.nodeErrors += 1
155
156    # Convert OSM tags into styles being used in output format
157    styles = self.wayStyle(tags)
158    #print ", ".join(["%s = %d" % (a['name'], self.enumIDs[a['name']]) for a in styles])
159
160    data = ''
161    for style in styles:
162      data += self.packWay(wid, waynodes, style, tags)
163    if(data):
164      for tile in tilesTouched.keys():
165        filename = self.filename(tile)
166        f = self.files.fp(filename)
167        if(f):
168          f.write(data)
169      self.average.add(len(data))
170
171  def filename(self,tile):
172    (x,y) = [int(i) for i in tile.split("_")]
173    tx = int(x / 64)
174    ty = int(y / 64)
175    directory = "output/%d_%d/" % (tx,ty)
176    if(not os.path.exists(directory)):
177      os.mkdir(directory)
178    return("%s/%s.bin" % (directory,tile))
179   
180 
181  def packWay(self, id, nodes, style, tags):
182    """Compress a way for storage"""
183    data = ''
184   
185    data += struct.pack("I", id)
186   
187    # Nodes
188    data += struct.pack("I", len(nodes))
189    for n in nodes:
190      (id,lat,lon) = n
191      (x,y) = tilenames.tileXY(lat,lon,31)
192      data += struct.pack("III", x, y, id)
193
194    # Styles
195    data += struct.pack("I", self.enumIDs[style['name']])
196
197    # Layer
198    try:
199      layer = int(tags.get('layer',0))
200    except ValueError:  # yeah thanks for whoever entered "=1" as a layer number...
201      layer = 0
202    data += struct.pack("b", layer)
203
204    # Important tags
205    packedTags = ''
206    packedTags += self.packTag("N", tags, 'name')
207    packedTags += self.packTag("r", tags, 'ref')
208    packedTags += self.packTag("r", tags, 'ncn_ref')
209    data += struct.pack("H", len(packedTags)) + packedTags
210
211    return(data)
212   
213  def packTag(self, type, tags, tag):
214    string = tags.get(tag, None)
215    if(string):
216      string = str(string)
217      return(type + struct.pack("H", len(string)) + string)
218    return('')
219
220  def storePoi(self,id,tags, latlon):
221    """Store an interesting node"""
222    self.countPoi += 1
223
224 
225class osmParser(handler.ContentHandler):
226  def __init__(self, filename,db):
227    self.db = db
228    self.useless = ('',
229      'created_by',
230      'source',
231      'editor',
232      'ele',
233      'time',
234      'editor',
235      'author',
236      'hdop',
237      'pdop',
238      'sat',
239      'speed',
240      'fix',
241      'course',
242      'converted_by',
243      'attribution',
244      'upload_tag',
245      'history')
246       
247    try:
248      parser = make_parser()
249      parser.setContentHandler(self)
250      parser.parse(filename)
251    except xml.sax._exceptions.SAXParseException:
252      print "Error loading %s" % filename
253    print "Done\n%d nodes\n%d ways\n%d POIs\n%d nodes not found"% (
254      self.db.countNodes,
255      self.db.countWays,
256      self.db.countPoi,
257      self.db.nodeErrors)
258  def startElement(self, name, attrs):
259    """Handle XML elements"""
260    if name in('node','way','relation'):
261      self.tags = {}
262      self.isInteresting = False
263      self.waynodes = []
264      if name == 'node':
265        id = int(attrs.get('id'))
266        self.nodeID = id
267        lat = float(attrs.get('lat'))
268        lon = float(attrs.get('lon'))
269        self.nodepos = (lat,lon)
270        self.db.storeNode(id,lat,lon)
271      elif name == 'way':
272        id = int(attrs.get('id'))
273        self.wayID = id
274    elif name == 'nd':
275      """Nodes within a way -- add them to a list"""
276      node = int(attrs.get('ref'))
277      self.waynodes.append(node)
278    elif name == 'tag':
279      """Tags - store them in a hash"""
280      k,v = (attrs.get('k'), attrs.get('v'))
281     
282      # Test if a tag is interesting enough to make it worth
283      # storing this node as a special "point of interest"
284      if not k in self.useless:
285        if(k[0:5] != "tiger"):
286          self.tags[k] = v
287          self.isInteresting = True
288 
289  def endElement(self, name):
290    if name == 'way':
291      self.db.storeWay(self.wayID,self.waynodes,self.tags)
292     
293    elif name == 'node':
294      if(self.isInteresting):
295        self.db.storePoi(self.nodeID,self.tags, self.nodepos)
296
297
298if(__name__ == "__main__"):
299  filename = "data/nodes.dat"
300  db = dbStore(filename, os.path.exists(filename))
301  a = osmParser(sys.stdin, db)
302  db.cleanup()
303  print "%d ways, average length %1.0f bytes, max %d" % (db.average.count, db.average.average(), db.average.max)
Note: See TracBrowser for help on using the repository browser.