Skip to content

Commit

Permalink
First copy of tilestache-clean.py from tilestache-seed.py
Browse files Browse the repository at this point in the history
  • Loading branch information
migurski committed Jan 10, 2012
1 parent f49b896 commit 7f4f2e9
Showing 1 changed file with 195 additions and 0 deletions.
195 changes: 195 additions & 0 deletions scripts/tilestache-clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#!/usr/bin/env python
"""tilestache-clean.py will flush your cache.
This script is intended to be run directly. This example cleans the area around
West Oakland (http://sta.mn/ck) in the "osm" layer, for zoom levels 12-15:
tilestache-clean.py -c ./config.json -l osm -b 37.79 -122.35 37.83 -122.25 -e png 12 13 14 15
See `tilestache-clean.py --help` for more information.
"""

from sys import stderr, path
from optparse import OptionParser

try:
from json import dump as json_dump
except ImportError:
from simplejson import dump as json_dump

#
# Most imports can be found below, after the --include-path option is known.
#

parser = OptionParser(usage="""%prog [options] [zoom...]
Cleans a single layer in your TileStache configuration - no images are returned,
and TileStache ends up with an empty in selected areas cache. Bounding box is
given as a pair of lat/lon coordinates, e.g. "37.788 -122.349 37.833 -122.246".
Output is a list of tile paths as they are created.
Configuration, bbox, and layer options are required; see `%prog --help` for info.""")

defaults = dict(extension='png', padding=0, verbose=True, bbox=(37.777, -122.352, 37.839, -122.226))

parser.set_defaults(**defaults)

parser.add_option('-c', '--config', dest='config',
help='Path to configuration file.')

parser.add_option('-l', '--layer', dest='layer',
help='Layer name from configuration.')

parser.add_option('-b', '--bbox', dest='bbox',
help='Bounding box in floating point geographic coordinates: south west north east.',
type='float', nargs=4)

parser.add_option('-p', '--padding', dest='padding',
help='Extra margin of tiles to add around bounded area. Default value is %s (no extra tiles).' % repr(defaults['padding']),
type='int')

parser.add_option('-e', '--extension', dest='extension',
help='Optional file type for rendered tiles. Default value is %s.' % repr(defaults['extension']))

parser.add_option('-f', '--progress-file', dest='progressfile',
help="Optional JSON progress file that gets written on each iteration, so you don't have to pay close attention.")

parser.add_option('-q', action='store_false', dest='verbose',
help='Suppress chatty output, --progress-file works well with this.')

parser.add_option('-i', '--include-path', dest='include',
help="Add the following colon-separated list of paths to Python's include path (aka sys.path)")

parser.add_option('--tile-list', dest='tile_list',
help='Optional file of tile coordinates, a simple text list of Z/X/Y coordinates. Overrides --bbox and --padding.')

def generateCoordinates(ul, lr, zooms, padding):
""" Generate a stream of (offset, count, coordinate) tuples for seeding.
Flood-fill coordinates based on two corners, a list of zooms and padding.
"""
# start with a simple total of all the coordinates we will need.
count = 0

for zoom in zooms:
ul_ = ul.zoomTo(zoom).container().left(padding).up(padding)
lr_ = lr.zoomTo(zoom).container().right(padding).down(padding)

rows = lr_.row + 1 - ul_.row
cols = lr_.column + 1 - ul_.column

count += int(rows * cols)

# now generate the actual coordinates.
# offset starts at zero
offset = 0

for zoom in zooms:
ul_ = ul.zoomTo(zoom).container().left(padding).up(padding)
lr_ = lr.zoomTo(zoom).container().right(padding).down(padding)

for row in range(int(ul_.row), int(lr_.row + 1)):
for column in range(int(ul_.column), int(lr_.column + 1)):
coord = Coordinate(row, column, zoom)

yield (offset, count, coord)

offset += 1

def listCoordinates(filename):
""" Generate a stream of (offset, count, coordinate) tuples for seeding.
Read coordinates from a file with one Z/X/Y coordinate per line.
"""
coords = (line.strip().split('/') for line in open(filename, 'r'))
coords = (map(int, (row, column, zoom)) for (zoom, column, row) in coords)
coords = [Coordinate(*args) for args in coords]

count = len(coords)

for (offset, coord) in enumerate(coords):
yield (offset, count, coord)

if __name__ == '__main__':
options, zooms = parser.parse_args()

if options.include:
for p in options.include.split(':'):
path.insert(0, p)

from TileStache import parseConfigfile, getTile
from TileStache.Core import KnownUnknown
from TileStache.Caches import Disk, Multi

from ModestMaps.Core import Coordinate
from ModestMaps.Geo import Location

try:
if options.config is None:
raise KnownUnknown('Missing required configuration (--config) parameter.')

if options.layer is None:
raise KnownUnknown('Missing required layer (--layer) parameter.')

config = parseConfigfile(options.config)

if options.layer not in config.layers:
raise KnownUnknown('"%s" is not a layer I know about. Here are some that I do know about: %s.' % (options.layer, ', '.join(sorted(config.layers.keys()))))

layer = config.layers[options.layer]
layer.write_cache = True # Override to make seeding guaranteed useful.

verbose = options.verbose
extension = options.extension
progressfile = options.progressfile

lat1, lon1, lat2, lon2 = options.bbox
south, west = min(lat1, lat2), min(lon1, lon2)
north, east = max(lat1, lat2), max(lon1, lon2)

northwest = Location(north, west)
southeast = Location(south, east)

ul = layer.projection.locationCoordinate(northwest)
lr = layer.projection.locationCoordinate(southeast)

for (i, zoom) in enumerate(zooms):
if not zoom.isdigit():
raise KnownUnknown('"%s" is not a valid numeric zoom level.' % zoom)

zooms[i] = int(zoom)

if options.padding < 0:
raise KnownUnknown('A negative padding will not work.')

padding = options.padding
tile_list = options.tile_list

except KnownUnknown, e:
parser.error(str(e))

if tile_list:
coordinates = listCoordinates(tile_list)
else:
coordinates = generateCoordinates(ul, lr, zooms, padding)

for (offset, count, coord) in coordinates:
path = '%s/%d/%d/%d.%s' % (layer.name(), coord.zoom, coord.column, coord.row, extension)

progress = {"tile": path,
"offset": offset + 1,
"total": count}

if options.verbose:
print >> stderr, '%(offset)d of %(total)d...' % progress,

mimetype, format = layer.getTypeByExtension(extension)
config.cache.remove(layer, coord, format)

if options.verbose:
print >> stderr, '%(tile)s' % progress

if progressfile:
fp = open(progressfile, 'w')
json_dump(progress, fp)
fp.close()

0 comments on commit 7f4f2e9

Please sign in to comment.