Subversion Repositories Misc

[/] [dslmap/] [forgottenislanderbot/] [fib-dbutil.py] - Blame information for rev 15

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 15 art
#!/usr/bin/env python
2
 
3
# forgottenislanderbot - makes a DSL coverage map from Bell Aliant's website.
4
# Copyright (c) 2010 Art Ortenburger
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
#
20
 
21
# fib-dbutil.py
22
# by Art Ortenburger IV <utrrrongeeb a users , sf , net>
23
# written 2010-01-14 -
24
#
25
# forgottenislanderbot -- database utility
26
#
27
# Does various operations on FIB sqlite databases,
28
# including merging, gathering statistics, and
29
# generating Google Earth KML files.
30
#
31
 
32
 
33
import sys
34
import sqlite3
35
import fibmod
36
 
37
print sys.argv[0],"""(forgottenislanderbot database utility)    Version 0.1
38
Copyright (c) 2010 Art Ortenburger. Licensed under the GNU GPL; no warranty."""
39
 
40
 
41
def mergeDBs(outDB,inDB1,inDB2):
42
    """ merges two SQlite databases into a third.
43
    all params must be sqlite3.Cursor objects."""
44
    print "not implemented"
45
    # open input dbs
46
    # figure out how to merge dbs, etc
47
 
48
 
49
# kml file to write
50
kmlf = None
51
# five-digit dsl status titles for output table and list printing
52
outputnames = ["ERROR","EMPTY","NODSL","BASIC","ULTRA","TOTAL"]
53
 
54
def genKML(inDB,dslflags,sparsity,exclude,outkml):
55
    """ Generates a KML file from a database.
56
    inDB:       sqlite3.Cursor to FIB database.
57
    dslflags:   4 y/n charbools in string sequence.
58
                Whether to include in KML output
59
                each of EMPTY, NODSL, BASIC, & ULTRA
60
                categories. Example: "nyyy"
61
    sparsity:   int; minimum civic-number distance
62
                between houses to output. This is
63
                to avoid slowing Google Earth to a
64
                crawl with 68023 placemarks.
65
    exclude:    bool; whether to exclude PEI cities from output
66
    outkml:     string; path to KML file to write.
67
                Will be overwritten."""
68
    try:
69
        globals()['kmlf'] = open(outkml,"wb")
70
        # @77 consider NASA World Wind support with different header, footer
71
        kmlf.write(KML_HEADER) # static KML header, with styles
72
        # have module function loop through database, adding addresses
73
        # current maxrows is False, for minimum returns; change that?
74
        fibmod.processDB(sqCur,kmlItemAction,dslflags,False,sparsity,0,exclude)
75
        kmlf.write(KML_FOOTER) # static KML footer
76
        kmlf.flush()
77
        kmlf.close()
78
    except Exception, errdet:
79
        print "Error writing KML file:", errdet
80
        safeexit()
81
 
82
 
83
 
84
def printStats(inDB,dslflags,sparsity,exclude):
85
    """ Prints statistics from civic address database.
86
    inDB:       sqlite3.Cursor to FIB database
87
    sparsity:   int; minimum civic-number distance
88
                between houses to output. This is
89
                to find out what a given sparsity
90
                will do to the crawler or Google Earth.
91
                For totals, use 1.
92
    exclude:    bool; whether to exclude PEI cities from output"""
93
    # have module function loop through database, counting stats
94
    # at the moment maxrows is False, for minimum returns; change it?
95
    fibmod.processDB(sqCur,statItemAction,dslflags,False,sparsity,0,exclude)
96
    print # newline needed
97
    # print little table
98
    for i in range(6):
99
        print outputnames[i],":         ",stats[i]
100
 
101
 
102
def printList(inDB,dslflags,sparsity,exclude):
103
    """ Prints addresses from civic address database.
104
    inDB:       sqlite3.Cursor to FIB database
105
    sparsity:   int; minimum civic-number distance
106
                between houses to output. This is
107
                to find out what a given sparsity
108
                will do to the crawler or Google Earth.
109
                For totals, use 1.
110
    exclude:    bool; whether to exclude PEI cities from output"""
111
    # have module function loop through database, calling handler
112
    # function listItemAction() to print each one.
113
    # at the moment maxrows is False, for minimum returns; change it?
114
    fibmod.processDB(sqCur,listItemAction,dslflags,False,sparsity,0,exclude)
115
    sys.stdout.flush()
116
 
117
 
118
def kmlItemAction(sqlcur,town,road,civic,delay,status,lon,lat):
119
    """ handles an individual selected street address
120
        Called by fibmod.processDB, from genKML().
121
        Does not honour delay param.
122
        See fibmod.processDB for param description."""
123
    # obsolete due to dslFlags being moved to fibmod.processDB()
124
##    # some statuses will be excluded; just skip them
125
##    if not dslFlags[status]:
126
##        return;
127
    # write nicely formatted data to file.
128
    kmlf.write("""
129
                <Placemark>
130
                        <styleUrl>#msn_"""+kmlIconMap[status]+"""-pushpin</styleUrl>
131
                        <Point>
132
                                <coordinates>"""+str(lon)+""","""+str(lat)+""",0</coordinates>
133
                        </Point>
134
                </Placemark>
135
""")
136
    # The only useful bit in this block is the <name> block.
137
    # However, this makes Google Earth rather cluttered,
138
    # adds to file size, and raises privacy concerns.
139
    # The '&' replacement is very important -- Earth chokes easily.
140
    # The <LookAt> block is useless bloat.
141
    # This stuff was originally above <styleUrl>.
142
##                      <name>"""+str(civic)+" "+road.replace("&","&amp;")+", "+town+"""</name>
143
##                      <LookAt>
144
##                              <longitude>"""+str(lon)+"""</longitude>
145
##                              <latitude>"""+str(lat)+"""</latitude>
146
##                              <altitude>0</altitude>
147
##                              <range>600</range>
148
##                              <tilt>0</tilt>
149
##                              <heading>0</heading>
150
##                              <altitudeMode>relativeToGround</altitudeMode>
151
##                              <gx:altitudeMode>relativeToSeaFloor</gx:altitudeMode>
152
##                      </LookAt>
153
    # end of kmlItemAction()
154
 
155
 
156
def statItemAction(sqlcur,town,road,civic,delay,status,lon,lat):
157
    """ handles an individual selected street address.
158
        Called by fibmod.processDB, from printStats().
159
        Does not honour delay param. For param details,
160
        see docstring for fibmod.processDB()."""
161
    stats[5] = stats[5] + 1 # increment the total
162
    if status < 0: # error
163
        stats[0] = stats[0] + 1
164
    elif status == 0: # unknown [EMPTY]
165
        stats[1] = stats[1] + 1
166
    elif status == 1: # no [NODSL]
167
        stats[2] = stats[2] + 1
168
    elif status == 2: # basic [BASIC]
169
        stats[3] = stats[3] + 1
170
    elif status == 3: # ultra [ULTRA]
171
        stats[4] = stats[4] + 1
172
    else: # error
173
        print "invalid status: ",civic, road,",", town,": ",status
174
 
175
def listItemAction(sqlcur,town,road,civic,delay,status,lon,lat):
176
    """ prints an individual selected street address.
177
        Called by fibmod.processDB, from printList().
178
        Does not honour delay param. For param details,
179
        see docstring for fibmod.processDB()."""
180
    print outputnames[(status+1)],"     ",civic,road,",",town
181
 
182
 
183
 
184
# sqlite3 database connection
185
sqCon = None
186
# sqlite3 database cursor
187
sqCur = None
188
 
189
# dsl status mask, as used by various functions. EMPTY, NODSL, BASIC, ULTRA
190
dslFlags = [ False, False, False, False ]
191
 
192
# cumulated statistics. Incremented by statItemAction(), through printStats()
193
stats = [0, 0, 0, 0, 0, 0] # error, unknown, no, basic, ultra, total
194
 
195
# truncated codes for Google Earth's KML URL pushpin colors.
196
# EMPTY, NODSL, BASIC, ULTRA
197
kmlIconMap = ["wht","ylw","ltblu","grn"]
198
 
199
 
200
 
201
def safeexit():
202
    """ Exits the database utility safely,
203
        committing, flushing, and closing anything that needs it."""
204
    if kmlf != None:
205
        try:
206
            kmlf.flush()
207
        except:
208
            pass
209
        kmlf.close()
210
    try:
211
        sqCur.close()
212
    except:
213
        pass
214
    try:
215
        sqCon.commit()
216
        sqCon.close()
217
    except:
218
        pass
219
 
220
# help message to print if necessary
221
HELPSTR = """
222
Usage:        fib-dbutil.py database.sqlite (COMMAND) (OPTIONS)
223
 
224
    (COMMANDS)    (OPTIONS)
225
    --merge       input1.sqlite input2.sqlite
226
        Merges the two databases into the new one, producing the widest
227
        range of addresses, with the highest levels of service.
228
    --kml         (dslmap) (sparsity) (exclude) output.kml
229
        Renders a Google Earth KML file from the database.
230
    --stats       (dslmap) (sparsity) (exclude)
231
        Prints the total number of addresses for each service.
232
    --list        (dslmap) (sparsity) (exclude)
233
        Prints the addresses for each service.
234
            Shared options:
235
    dslmap - which services to show. Four y/n flags,
236
        respectively unknown/no/basic/ultra.
237
    sparsity - minimum civic-number difference along a road
238
        in a community to skip.
239
    exclude - exclude cities or not (y/n)."""
240
 
241
# static header for Google Earth KML
242
KML_HEADER = """<?xml version="1.0" encoding="UTF-8"?>
243
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
244
<Document>
245
        <name>forgottenislanderbot</name>
246
        <Style id="sn_ltblu-pushpin">
247
                <IconStyle>
248
                        <scale>1.1</scale>
249
                        <Icon>
250
                                <href>http://maps.google.com/mapfiles/kml/pushpin/ltblu-pushpin.png</href>
251
                        </Icon>
252
                        <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
253
                </IconStyle>
254
                <ListStyle>
255
                </ListStyle>
256
        </Style>
257
        <StyleMap id="msn_wht-pushpin">
258
                <Pair>
259
                        <key>normal</key>
260
                        <styleUrl>#sn_wht-pushpin</styleUrl>
261
                </Pair>
262
                <Pair>
263
                        <key>highlight</key>
264
                        <styleUrl>#sh_wht-pushpin</styleUrl>
265
                </Pair>
266
        </StyleMap>
267
        <Style id="sn_wht-pushpin">
268
                <IconStyle>
269
                        <scale>1.1</scale>
270
                        <Icon>
271
                                <href>http://maps.google.com/mapfiles/kml/pushpin/wht-pushpin.png</href>
272
                        </Icon>
273
                        <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
274
                </IconStyle>
275
                <ListStyle>
276
                </ListStyle>
277
        </Style>
278
        <Style id="sh_ylw-pushpin">
279
                <IconStyle>
280
                        <scale>1.3</scale>
281
                        <Icon>
282
                                <href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
283
                        </Icon>
284
                        <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
285
                </IconStyle>
286
                <ListStyle>
287
                </ListStyle>
288
        </Style>
289
        <Style id="sh_ltblu-pushpin">
290
                <IconStyle>
291
                        <scale>1.3</scale>
292
                        <Icon>
293
                                <href>http://maps.google.com/mapfiles/kml/pushpin/ltblu-pushpin.png</href>
294
                        </Icon>
295
                        <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
296
                </IconStyle>
297
                <ListStyle>
298
                </ListStyle>
299
        </Style>
300
        <StyleMap id="msn_ltblu-pushpin">
301
                <Pair>
302
                        <key>normal</key>
303
                        <styleUrl>#sn_ltblu-pushpin</styleUrl>
304
                </Pair>
305
                <Pair>
306
                        <key>highlight</key>
307
                        <styleUrl>#sh_ltblu-pushpin</styleUrl>
308
                </Pair>
309
        </StyleMap>
310
        <Style id="sn_grn-pushpin">
311
                <IconStyle>
312
                        <scale>1.1</scale>
313
                        <Icon>
314
                                <href>http://maps.google.com/mapfiles/kml/pushpin/grn-pushpin.png</href>
315
                        </Icon>
316
                        <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
317
                </IconStyle>
318
                <ListStyle>
319
                </ListStyle>
320
        </Style>
321
        <StyleMap id="msn_ylw-pushpin">
322
                <Pair>
323
                        <key>normal</key>
324
                        <styleUrl>#sn_ylw-pushpin</styleUrl>
325
                </Pair>
326
                <Pair>
327
                        <key>highlight</key>
328
                        <styleUrl>#sh_ylw-pushpin</styleUrl>
329
                </Pair>
330
        </StyleMap>
331
        <StyleMap id="msn_grn-pushpin">
332
                <Pair>
333
                        <key>normal</key>
334
                        <styleUrl>#sn_grn-pushpin</styleUrl>
335
                </Pair>
336
                <Pair>
337
                        <key>highlight</key>
338
                        <styleUrl>#sh_grn-pushpin</styleUrl>
339
                </Pair>
340
        </StyleMap>
341
        <Style id="sn_ylw-pushpin">
342
                <IconStyle>
343
                        <scale>1.1</scale>
344
                        <Icon>
345
                                <href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
346
                        </Icon>
347
                        <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
348
                </IconStyle>
349
                <ListStyle>
350
                </ListStyle>
351
        </Style>
352
        <Style id="sh_grn-pushpin">
353
                <IconStyle>
354
                        <scale>1.3</scale>
355
                        <Icon>
356
                                <href>http://maps.google.com/mapfiles/kml/pushpin/grn-pushpin.png</href>
357
                        </Icon>
358
                        <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
359
                </IconStyle>
360
                <ListStyle>
361
                </ListStyle>
362
        </Style>
363
        <Style id="sh_wht-pushpin">
364
                <IconStyle>
365
                        <scale>1.3</scale>
366
                        <Icon>
367
                                <href>http://maps.google.com/mapfiles/kml/pushpin/wht-pushpin.png</href>
368
                        </Icon>
369
                        <hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
370
                </IconStyle>
371
                <ListStyle>
372
                </ListStyle>
373
        </Style>
374
        <Folder>
375
                <name>forgottenislanderbot</name>
376
                <open>0</open>
377
 
378
"""
379
 
380
# static footer for Google Earth KML
381
KML_FOOTER = """
382
 
383
        </Folder>
384
</Document>
385
</kml>
386
 
387
"""
388
 
389
 
390
# no command takes less than four args, so assume exit. Maybe room to improve?
391
if len(sys.argv) < 5:
392
    print HELPSTR
393
    exit()
394
 
395
# need to load the database here
396
sqCon = sqlite3.connect(sys.argv[1])
397
sqCur = sqCon.cursor()
398
 
399
# parse for various cmd-line flags
400
if sys.argv[2] == "--merge":# not implemented
401
    mergeDBs(sys.argv[1],sys.argv[3],sys.argv[4])
402
elif sys.argv[2] == "--kml":
403
    if len(sys.argv) < 7:
404
        print HELPSTR
405
        exit()
406
    genKML(sys.argv[1],sys.argv[3],int(sys.argv[4]),fibmod.char2Bool(sys.argv[5]),sys.argv[6])
407
elif sys.argv[2] == "--stats":
408
    if len(sys.argv) < 6:
409
        print HELPSTR
410
        exit()
411
    printStats(sys.argv[1],sys.argv[3],int(sys.argv[4]),fibmod.char2Bool(sys.argv[5]))
412
elif sys.argv[2] == "--list":
413
    if len(sys.argv) < 6:
414
           print HELPSTR
415
           exit()
416
    printList(sys.argv[1],sys.argv[3],int(sys.argv[4]),fibmod.char2Bool(sys.argv[5]))
417
else:
418
    print HELPSTR
419
    exit()
420
 
421
# after everything's done, make sure everything's saved
422
safeexit()
423
 
424