PaxHeader/tilecache-2.11000755 777777 777777 00000000257 11456036476 017125 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142716 38 LIBARCHIVE.creationtime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311395 19 SCHILY.nlink=16 tilecache-2.11/000755 473017214730172100000000000 11456036476 015360 5ustar00christoschristos000000 000000 tilecache-2.11/PaxHeader/dev.ini000644 777777 777777 00000000210 11205506201 020417 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311466 18 SCHILY.nlink=1 tilecache-2.11/dev.ini000644 473017214730172100000000441 11205506201 016612 0ustar00christoschristos000000 000000 [server:main] #tested with Paste#http and PasteScript#wsgiutils, PasteScript#twisted also possible use = egg:PasteScript#cherrypy host = 127.0.0.1 port = 5000 [composite:main] use = egg:Paste#urlmap /tc = tilecache1 [app:tilecache1] use = egg:TileCache tilecache_config = tilecache.cfg tilecache-2.11/PaxHeader/docs000755 777777 777777 00000000257 11456036476 020055 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142717 38 LIBARCHIVE.creationtime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311445 19 SCHILY.nlink=16 tilecache-2.11/docs/000755 473017214730172100000000000 11456036476 016310 5ustar00christoschristos000000 000000 tilecache-2.11/PaxHeader/ez_setup.py000644 777777 777777 00000000210 10733205325 021360 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311443 18 SCHILY.nlink=1 tilecache-2.11/ez_setup.py000644 473017214730172100000021400 10733205325 017551 0ustar00christoschristos000000 000000 #!python """Bootstrap setuptools installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from ez_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import sys DEFAULT_VERSION = "0.6c7" DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] md5_data = { 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', } import sys, os def _validate_md5(egg_name, data): if egg_name in md5_data: from md5 import md5 digest = md5(data).hexdigest() if digest != md5_data[egg_name]: print >>sys.stderr, ( "md5 validation of %s failed! (Possible download problem?)" % egg_name ) sys.exit(2) return data def use_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15 ): """Automatically find/download setuptools and make it available on sys.path `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where setuptools will be downloaded, if it is not already available. If `download_delay` is specified, it should be the number of seconds that will be paused before initiating a download, should one be required. If an older version of setuptools is installed, this routine will print a message to ``sys.stderr`` and raise SystemExit in an attempt to abort the calling script. """ try: import setuptools if setuptools.__version__ == '0.0.1': print >>sys.stderr, ( "You have an obsolete version of setuptools installed. Please\n" "remove it from your system entirely before rerunning this script." ) sys.exit(2) except ImportError: egg = download_setuptools(version, download_base, to_dir, download_delay) sys.path.insert(0, egg) import setuptools; setuptools.bootstrap_install_from = egg import pkg_resources try: pkg_resources.require("setuptools>="+version) except pkg_resources.VersionConflict, e: # XXX could we install in a subprocess here? print >>sys.stderr, ( "The required version of setuptools (>=%s) is not available, and\n" "can't be installed while this script is running. Please install\n" " a more recent version first.\n\n(Currently using %r)" ) % (version, e.args[0]) sys.exit(2) def download_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay = 15 ): """Download setuptools from a specified location and return its filename `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ import urllib2, shutil egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) url = download_base + egg_name saveto = os.path.join(to_dir, egg_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: from distutils import log if delay: log.warn(""" --------------------------------------------------------------------------- This script requires setuptools version %s to run (even to display help). I will attempt to download it for you (from %s), but you may need to enable firewall access for this script first. I will start the download in %d seconds. (Note: if this machine does not have network access, please obtain the file %s and place it in this directory before rerunning this script.) ---------------------------------------------------------------------------""", version, download_base, delay, url ); from time import sleep; sleep(delay) log.warn("Downloading %s", url) src = urllib2.urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = _validate_md5(egg_name, src.read()) dst = open(saveto,"wb"); dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" try: import setuptools except ImportError: egg = None try: egg = download_setuptools(version, delay=0) sys.path.insert(0,egg) from setuptools.command.easy_install import main return main(list(argv)+[egg]) # we're done here finally: if egg and os.path.exists(egg): os.unlink(egg) else: if setuptools.__version__ == '0.0.1': # tell the user to uninstall obsolete version use_setuptools(version) req = "setuptools>="+version import pkg_resources try: pkg_resources.require(req) except pkg_resources.VersionConflict: try: from setuptools.command.easy_install import main except ImportError: from easy_install import main main(list(argv)+[download_setuptools(delay=0)]) sys.exit(0) # try to force an exit else: if argv: from setuptools.command.easy_install import main main(argv) else: print "Setuptools version",version,"or greater has been installed." print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' def update_md5(filenames): """Update our built-in md5 registry""" import re from md5 import md5 for name in filenames: base = os.path.basename(name) f = open(name,'rb') md5_data[base] = md5(f.read()).hexdigest() f.close() data = [" %r: %r,\n" % it for it in md5_data.items()] data.sort() repl = "".join(data) import inspect srcfile = inspect.getsourcefile(sys.modules[__name__]) f = open(srcfile, 'rb'); src = f.read(); f.close() match = re.search("\nmd5_data = {\n([^}]+)}", src) if not match: print >>sys.stderr, "Internal error!" sys.exit(2) src = src[:match.start(1)] + repl + src[match.end(1):] f = open(srcfile,'w') f.write(src) f.close() if __name__=='__main__': if len(sys.argv)>2 and sys.argv[1]=='--md5update': update_md5(sys.argv[2:]) else: main(sys.argv[1:]) tilecache-2.11/PaxHeader/index.html000644 777777 777777 00000000210 10733214415 021145 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311464 18 SCHILY.nlink=1 tilecache-2.11/index.html000644 473017214730172100000002113 10733214415 017336 0ustar00christoschristos000000 000000
From MetaCarta Labs.
tilecache-2.11/PaxHeader/setup.py000644 777777 777777 00000000210 11456031665 020671 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311444 18 SCHILY.nlink=1 tilecache-2.11/setup.py000644 473017214730172100000003233 11456031665 017066 0ustar00christoschristos000000 000000 #!/usr/bin/env python import sys try: from setuptools import setup except: from ez_setup import use_setuptools use_setuptools() from setuptools import setup readme = file('docs/README.txt','rb').read() classifiers = [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Scientific/Engineering :: GIS', ] # We'd like to let debian install the /etc/tilecache.cfg, # but put them in tilecache/tilecache.cfg using setuptools # otherwise. extra = { } if "--debian" in sys.argv: extra['data_files']=[('/etc', ['tilecache.cfg']),('.',['dev.ini'])] sys.argv.remove("--debian") else: extra['data_files']=[('TileCache', ['tilecache.cfg']),('.',['dev.ini'])] setup(name='TileCache', version='2.11', description='a web map tile caching system', author='TileCache Contributors', author_email='tilecache@lists.osgeo.org', url='http://tilecache.org/', long_description=readme, packages=['TileCache', 'TileCache.Caches', 'TileCache.Services', 'TileCache.Layers'], scripts=['tilecache.cgi', 'tilecache.fcgi', 'tilecache_seed.py', 'tilecache_install_config.py', 'tilecache_clean.py', 'tilecache_http_server.py'], zip_safe=False, test_suite = 'tests.run_doc_tests', license="BSD", classifiers=classifiers, entry_points = """ [paste.app_factory] main = TileCache.Service:paste_deploy_app """, **extra ) tilecache-2.11/PaxHeader/tests000755 777777 777777 00000000210 11456036475 020253 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311435 18 SCHILY.nlink=7 tilecache-2.11/tests/000755 473017214730172100000000000 11456036475 016521 5ustar00christoschristos000000 000000 tilecache-2.11/PaxHeader/TileCache000755 777777 777777 00000000257 11456036475 020745 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142716 38 LIBARCHIVE.creationtime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311397 19 SCHILY.nlink=12 tilecache-2.11/TileCache/000755 473017214730172100000000000 11456036475 017200 5ustar00christoschristos000000 000000 tilecache-2.11/PaxHeader/tilecache.cfg000644 777777 777777 00000000210 11065024774 021560 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311396 18 SCHILY.nlink=1 tilecache-2.11/tilecache.cfg000644 473017214730172100000006516 11065024774 017764 0ustar00christoschristos000000 000000 # Configuration for MC TileCache # TileCache can load Layers or Caches from anywhere in sys.path. If you # prefer to load from somewhere which is *not* on sys.path, you can use # the path configuration paramter to set a comma-seperated list of # filesystem paths which you want prepended to sys.path. #[tilecache_options] #path=/home/you # Some TileCache options are controlled by metadata. One example is the # crossdomain_sites option, which allows you to add sites which are then # included in a crossdomain.xml file served from the root of the TileCache #[metadata] #crossdomain_sites=openstreetmap.org,openaerialmap.org # [cache] section examples: (mandatory!) # # Disk: # [cache] # type=Disk (works out of the box) # base= # # Memcached: # [cache] # type=Memcached (you'll need memcache.py and memcached running!) # servers=192.168.1.1:11211 # # Amazon S3: # [cache] # type=AWSS3 # access_key=your_access_key # secret_access_key=your_secret_access_key [cache] type=Disk base=/tmp/tilecache # [layername] -- all other sections are named layers # # type={MapServerLayer,WMSLayer} # *** if you want to use MapServerLayer, you *must* have Python mapscript # installed and available *** # # mapfile= # url= # layers=[,,,...] # *** optional iff layername if what # your data source calls the layer ** # extension={png,jpeg,gif} *** defaults to "png" *** # size=256,256 *** defaults to 256x256 *** # bbox=-180.0,-90.0,180.0,90.0 *** defaults to world in lon/lat *** # srs=EPSG:4326 *** defaults to EPSG:4326 *** # levels=20 *** defaults to 20 zoom levels *** # resolutions=0.1,0.05,0.025,... *** defaults to global profile *** # metaTile=true *** metatiling off by default # requires python-imaging *** # metaSize=5,5 *** size of metatile in tiles # defaults to 5 x 5 *** # metaBuffer=10 *** size of metatile buffer in px *** # mime_type=image/png *** by default, the mime type is image/extension *** # *** but you may want to set extension=png8 for *** # *** GeoServer WMS, and this lets you set the *** # *** mime_type seperately. *** # The following is a demonstration of a layer which would be generated # according to the 'Google projection'. This uses the standard values for # a spherical mercator projection for maxextent, maxresolution, units # and srs. # [google-tiles] # type=WMS # url=http://localhost/cgi-bin/mapserv?map=/mapdata/world.map # layers=world # spherical_mercator=true # Standard MapServer layer configuration. # [vmap0] # type=MapServer # layers=vmap0 # mapfile=/var/www/vmap0.map # Rendering OpenStreetMap data with Mapnik; should use metaTiling to # avoid labels across tile boundaries # [osm] # type=Mapnik # mapfile=/home/user/osm-mapnik/osm.xml # spherical_mercator=true # tms_type=google # metatile=yes [basic] type=WMS url=http://labs.metacarta.com/wms/vmap0 extension=png tilecache-2.11/PaxHeader/tilecache.cgi000755 777777 777777 00000000210 10600505762 021561 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311441 18 SCHILY.nlink=1 tilecache-2.11/tilecache.cgi000755 473017214730172100000000236 10600505762 017756 0ustar00christoschristos000000 000000 #!/usr/bin/env python from TileCache import Service, cgiHandler, cfgfiles if __name__ == '__main__': svc = Service.load(*cfgfiles) cgiHandler(svc) tilecache-2.11/PaxHeader/tilecache.fcgi000644 777777 777777 00000000210 10571173007 021724 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311462 18 SCHILY.nlink=1 tilecache-2.11/tilecache.fcgi000644 473017214730172100000000244 10571173007 020120 0ustar00christoschristos000000 000000 #!/usr/bin/python from TileCache.Service import wsgiApp if __name__ == '__main__': from flup.server.fcgi_fork import WSGIServer WSGIServer(wsgiApp).run() tilecache-2.11/PaxHeader/tilecache_clean.py000755 777777 777777 00000000210 11456032102 022602 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311465 18 SCHILY.nlink=1 tilecache-2.11/tilecache_clean.py000755 473017214730172100000005360 11456032102 021002 0ustar00christoschristos000000 000000 #!/usr/bin/env python # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors """This is intended to be run as a command line tool. Use --help to determine arguments to this program. It is a tool to keep a certain directory to a maximum size by removing least recently accessed files from the directory.""" import sys, os, heapq, time from optparse import OptionParser MAX_HEAP_SIZE = 1000000 MAX_CACHE_SIZE = 500 # MB def walk_disk_cache (rootdir, max_entries): """Walk a directory structure, building a heap of up to max_entries.""" heap = [] cache_size = 0 start = time.time() for root, dirs, files in os.walk(rootdir): for file in files: path = os.path.join(root, file) stat = os.stat(path) size = stat.st_size if hasattr(stat, 'st_blocks'): size = stat.st_blocks * stat.st_blksize # strip off rootdir to keep RAM use down path = path[len(rootdir):] heapq.heappush(heap, (stat.st_atime, size, path)) cache_size += size del heap[max_entries:] return heap, cache_size def clean_disk_cache (rootdir, max_size, max_entries): """Remove files from directory until its size is less than max_size (which is megabytes). Up to max_entries will be removed per-run.""" heap, cache_size = walk_disk_cache(rootdir, max_entries) max_size <<= 20 # megabytes print "Cache entries found: %d" % len(heap) removed_files = 0 while heap and cache_size > max_size: atime, size, path = heapq.heappop(heap) cache_size -= size path = rootdir + path try: os.unlink(path) removed_files += 1 except OSError, e: print >>sys.stderr, "Error removing tile %s: %s" % (path, e) print "Removed %d files." % removed_files if __name__ == "__main__": usage = "usage: %prog [options] " parser = OptionParser(usage=usage, version="%prog 1.0") parser.add_option("-s", "--size", action="store", type="int", dest="size", default = MAX_CACHE_SIZE, help="Maximum cache size, in megabytes.") parser.add_option("-e", "--entries", action="store", type="int", dest="entries", default=MAX_HEAP_SIZE, help="""Maximum cache entries. This limits the amount of memory that will be used to store information about tiles to remove.""") (options, args) = parser.parse_args() if not len(args): parser.error("Missing required cache_location argument.") clean_disk_cache(args[0], options.size, options.entries) tilecache-2.11/PaxHeader/tilecache_http_server.py000755 777777 777777 00000000210 10767604622 024105 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311467 18 SCHILY.nlink=1 tilecache-2.11/tilecache_http_server.py000755 473017214730172100000002113 10767604622 022276 0ustar00christoschristos000000 000000 #!/usr/bin/python from TileCache.Service import wsgiApp from optparse import OptionParser def run(port=8080, threading=False, config=None): from wsgiref import simple_server if threading: from SocketServer import ThreadingMixIn class myServer(ThreadingMixIn, simple_server.WSGIServer): pass httpd = myServer(('',port), simple_server.WSGIRequestHandler,) else: httpd = simple_server.WSGIServer(('',port), simple_server.WSGIRequestHandler,) httpd.set_app(wsgiApp) try: print "Listening on port %s" % port httpd.serve_forever() except KeyboardInterrupt: print "Shutting down." if __name__ == '__main__': parser = OptionParser() parser.add_option("-p", "--port", help="port to run webserver on. Default is 8080", dest="port", action='store', type="int", default=8080) parser.add_option("-t", "--threading", help="threading http server. default is false", dest="threading", action='store_true', default=False) (options, args) = parser.parse_args() run(options.port, options.threading) tilecache-2.11/PaxHeader/tilecache_install_config.py000644 777777 777777 00000000210 10733230256 024517 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311442 18 SCHILY.nlink=1 tilecache-2.11/tilecache_install_config.py000644 473017214730172100000002275 10733230256 022721 0ustar00christoschristos000000 000000 #!/usr/bin/python """Script to install a TileCache config from an egg installation.""" import sys from optparse import OptionParser def install(dest): try: f = open(dest, "w") except IOError, E: print "Unable to open destination file %s. Perhaps you need permission to write there?\n(Error was: %s)" % (dest, E) sys.exit(1) try: import pkg_resources filename = pkg_resources.resource_filename("TileCache", "tilecache.cfg") cfg = open(filename, "r") except Exception, E: print "Unable to open source file.\n(Error was: %s)" % (E) sys.exit(1) f.write(cfg.read()) f.close() cfg.close() print "Successfully copied file %s to %s." % (filename, dest) if __name__ == "__main__": parser = OptionParser(usage="""%prog [options] This script is a helper script designed to install the default TileCache configuration when TileCache is installed from an egg.""") parser.add_option('-d', '--dest', dest="dest", help="install to FILE. Default is /etc/tilecache.cfg", default="/etc/tilecache.cfg", metavar="FILE") (options, args) = parser.parse_args() install(options.dest) tilecache-2.11/PaxHeader/tilecache_seed.py000755 777777 777777 00000000210 11456032102 022440 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311463 18 SCHILY.nlink=1 tilecache-2.11/tilecache_seed.py000755 473017214730172100000000376 11456032102 020642 0ustar00christoschristos000000 000000 #!/usr/bin/env python # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors """This is intended to be run as a command line tool. See the accompanying README file or man page for details.""" import TileCache.Client TileCache.Client.main() tilecache-2.11/TileCache/PaxHeader/__init__.py000644 777777 777777 00000000210 11456032102 023074 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311430 18 SCHILY.nlink=1 tilecache-2.11/TileCache/__init__.py000644 473017214730172100000000222 11456032102 021264 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from Cache import * from Layer import * from Client import * from Service import * tilecache-2.11/TileCache/PaxHeader/Cache.py000644 777777 777777 00000000210 11456032102 022340 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311432 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Cache.py000644 473017214730172100000003062 11456032102 020535 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors import os, sys, time from warnings import warn class Cache (object): def __init__ (self, timeout = 30.0, stale_interval = 300.0, readonly = False, expire = False, sendfile = False, **kwargs): self.stale = float(stale_interval) self.timeout = float(timeout) self.readonly = readonly self.expire = expire self.sendfile = sendfile and sendfile.lower() in ["yes", "y", "t", "true"] if expire != False: self.expire = long(expire) def lock (self, tile, blocking = True): start_time = time.time() result = self.attemptLock(tile) if result: return True elif not blocking: return False while result is not True: if time.time() - start_time > self.timeout: raise Exception("You appear to have a stuck lock. You may wish to remove the lock named:\n%s" % self.getLockName(tile)) time.sleep(0.25) result = self.attemptLock(tile) return True def getLockName (self, tile): return self.getKey(tile) + ".lck" def getKey (self, tile): raise NotImplementedError() def attemptLock (self, tile): raise NotImplementedError() def unlock (self, tile): raise NotImplementedError() def get (self, tile): raise NotImplementedError() def set (self, tile, data): raise NotImplementedError() def delete(self, tile): raise NotImplementedError() tilecache-2.11/TileCache/PaxHeader/Caches000755 777777 777777 00000000211 11456036475 022121 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311421 19 SCHILY.nlink=10 tilecache-2.11/TileCache/Caches/000755 473017214730172100000000000 11456036475 020366 5ustar00christoschristos000000 000000 tilecache-2.11/TileCache/PaxHeader/Client.py000644 777777 777777 00000000210 11456032102 022553 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311420 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Client.py000644 473017214730172100000017271 11456032102 020757 0ustar00christoschristos000000 000000 #!/usr/bin/env python # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors import sys, urllib, urllib2, time, os, math import time import httplib try: from optparse import OptionParser except ImportError: OptionParser = False # setting this to True will exchange more useful error messages # for privacy, hiding URLs and error messages. HIDE_ALL = False class WMS (object): fields = ("bbox", "srs", "width", "height", "format", "layers", "styles") defaultParams = {'version': '1.1.1', 'request': 'GetMap', 'service': 'WMS'} __slots__ = ("base", "params", "client", "data", "response") def __init__ (self, base, params, user=None, password=None): self.base = base if self.base[-1] not in "?&": if "?" in self.base: self.base += "&" else: self.base += "?" self.params = {} if user is not None and password is not None: x = urllib2.HTTPPasswordMgrWithDefaultRealm() x.add_password(None, base, user, password) self.client = urllib2.build_opener() auth = urllib2.HTTPBasicAuthHandler(x) self.client = urllib2.build_opener(auth) else: self.client = urllib2.build_opener() for key, val in self.defaultParams.items(): if self.base.lower().rfind("%s=" % key.lower()) == -1: self.params[key] = val for key in self.fields: if params.has_key(key): self.params[key] = params[key] elif self.base.lower().rfind("%s=" % key.lower()) == -1: self.params[key] = "" def url (self): return self.base + urllib.urlencode(self.params) def fetch (self): urlrequest = urllib2.Request(self.url()) # urlrequest.add_header("User-Agent", # "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" ) response = None while response is None: try: response = self.client.open(urlrequest) data = response.read() # check to make sure that we have an image... msg = response.info() if msg.has_key("Content-Type"): ctype = msg['Content-Type'] if ctype[:5].lower() != 'image': if HIDE_ALL: raise Exception("Did not get image data back. (Adjust HIDE_ALL for more detail.)") else: raise Exception("Did not get image data back. \nURL: %s\nContent-Type Header: %s\nResponse: \n%s" % (self.url(), ctype, data)) except httplib.BadStatusLine: response = None # try again return data, response def setBBox (self, box): self.params["bbox"] = ",".join(map(str, box)) def seed (svc, layer, levels = (0, 5), bbox = None, padding = 0, force = False, reverse = False, delay = 0 ): from Layer import Tile try: padding = int(padding) except: raise Exception('Your padding parameter is %s, but should be an integer' % padding) if not bbox: bbox = layer.bbox start = time.time() total = 0 for z in range(*levels): bottomleft = layer.getClosestCell(z, bbox[0:2]) topright = layer.getClosestCell(z, bbox[2:4]) # Why Are we printing to sys.stderr??? It's not an error. # This causes a termination if run from cron or in background if shell is terminated #print >>sys.stderr, "###### %s, %s" % (bottomleft, topright) print "###### %s, %s" % (bottomleft, topright) zcount = 0 metaSize = layer.getMetaSize(z) ztiles = int(math.ceil(float(topright[1] - bottomleft[1]) / metaSize[0]) * math.ceil(float(topright[0] - bottomleft[0]) / metaSize[1])) if reverse: startX = topright[0] + metaSize[0] + (1 * padding) endX = bottomleft[0] - (1 * padding) stepX = -metaSize[0] startY = topright[1] + metaSize[1] + (1 * padding) endY = bottomleft[1] - (1 * padding) stepY = -metaSize[1] else: startX = bottomleft[0] - (1 * padding) endX = topright[0] + metaSize[0] + (1 * padding) stepX = metaSize[0] startY = bottomleft[1] - (1 * padding) endY = topright[1] + metaSize[1] + (1 * padding) stepY = metaSize[1] for y in range(startY, endY, stepY): for x in range(startX, endX, stepX): tileStart = time.time() tile = Tile(layer,x,y,z) bounds = tile.bounds() svc.renderTile(tile,force=force) total += 1 zcount += 1 box = "(%.4f %.4f %.4f %.4f)" % bounds print "%02d (%06d, %06d) = %s [%.4fs : %.3f/s] %s/%s" \ % (z,x,y, box, time.time() - tileStart, total / (time.time() - start + .0001), zcount, ztiles) if delay: time.sleep(delay) def main (): if not OptionParser: raise Exception("TileCache seeding requires optparse/OptionParser. Your Python may be too old.\nSend email to the mailing list \n(http://openlayers.org/mailman/listinfo/tilecache) about this problem for help.") usage = "usage: %prog [ ]" parser = OptionParser(usage=usage, version="%prog $Id: Client.py 406 2010-10-15 11:00:18Z crschmidt $") parser.add_option("-f","--force", action="store_true", dest="force", default = False, help="force recreation of tiles even if they are already in cache") parser.add_option("-b","--bbox",action="store", type="string", dest="bbox", default = None, help="restrict to specified bounding box") parser.add_option("-c", "--config", action="store", type="string", dest="tilecacheconfig", default=None, help="path to configuration file") parser.add_option("-d","--delay",action="store", type="int", dest="delay", default = 0, help="Delay time between requests.") parser.add_option("-p","--padding",action="store", type="int", dest="padding", default = 0, help="extra margin tiles to seed around target area. Defaults to 0 "+ "(some edge tiles might be missing). A value of 1 ensures all tiles "+ "will be created, but some tiles may be wholly outside your bbox") parser.add_option("-r","--reverse", action="store_true", dest="reverse", default = False, help="Reverse order of seeding tiles") (options, args) = parser.parse_args() if len(args)>3: parser.error("Incorrect number of arguments. bbox and padding are now options (-b and -p)") from Service import Service, cfgfiles from Layer import Layer cfgs = cfgfiles if options.tilecacheconfig: configFile = options.tilecacheconfig print "Config file set to %s" % (configFile) cfgs = cfgs + (configFile,) svc = Service.load(*cfgs) layer = svc.layers[args[0]] if options.bbox: bboxlist = map(float,options.bbox.split(",")) else: bboxlist=None if len(args)>1: seed(svc, layer, map(int, args[1:3]), bboxlist , padding=options.padding, force = options.force, reverse = options.reverse, delay=options.delay) else: for line in sys.stdin.readlines(): lat, lon, delta = map(float, line.split(",")) bbox = (lon - delta, lat - delta, lon + delta, lat + delta) print "===> %s <===" % (bbox,) seed(svc, layer, (5, 17), bbox , force = options.force ) if __name__ == '__main__': main() tilecache-2.11/TileCache/PaxHeader/Layer.py000644 777777 777777 00000000210 11456032102 022411 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311407 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Layer.py000644 473017214730172100000041625 11456032102 020615 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors import os, sys from warnings import warn from Client import WMS from Service import TileCacheException DEBUG = True class Tile (object): """ >>> l = Layer("name", maxresolution=0.019914, size="256,256") >>> t = Tile(l, 18, 20, 0) """ __slots__ = ( "layer", "x", "y", "z", "data" ) def __init__ (self, layer, x, y, z): """ >>> l = Layer("name", maxresolution=0.019914, size="256,256") >>> t = Tile(l, 18, 20, 0) >>> t.x 18 >>> t.y 20 >>> t.z 0 >>> print t.data None """ self.layer = layer self.x = x self.y = y self.z = z self.data = None def size (self): """ >>> l = Layer("name", maxresolution=0.019914, size="256,256") >>> t = Tile(l, 18, 20, 0) >>> t.size() [256, 256] """ return self.layer.size def bounds (self): """ >>> l = Layer("name", maxresolution=0.019914) >>> t = Tile(l, 18, 20, 0) >>> t.bounds() (-88.236288000000002, 11.959680000000006, -83.138303999999991, 17.057664000000003) """ res = self.layer.resolutions[self.z] minx = self.layer.bbox[0] + (res * self.x * self.layer.size[0]) miny = self.layer.bbox[1] + (res * self.y * self.layer.size[1]) maxx = self.layer.bbox[0] + (res * (self.x + 1) * self.layer.size[0]) maxy = self.layer.bbox[1] + (res * (self.y + 1) * self.layer.size[1]) return (minx, miny, maxx, maxy) def bbox (self): """ >>> l = Layer("name", maxresolution=0.019914) >>> t = Tile(l, 18, 20, 0) >>> t.bbox() '-88.236288,11.95968,-83.138304,17.057664' """ return ",".join(map(str, self.bounds())) class MetaTile (Tile): def actualSize (self): """ >>> l = MetaLayer("name") >>> t = MetaTile(l, 0,0,0) >>> t.actualSize() (256, 256) """ metaCols, metaRows = self.layer.getMetaSize(self.z) return ( self.layer.size[0] * metaCols, self.layer.size[1] * metaRows ) def size (self): actual = self.actualSize() return ( actual[0] + self.layer.metaBuffer[0] * 2, actual[1] + self.layer.metaBuffer[1] * 2 ) def bounds (self): tilesize = self.actualSize() res = self.layer.resolutions[self.z] buffer = (res * self.layer.metaBuffer[0], res * self.layer.metaBuffer[1]) metaWidth = res * tilesize[0] metaHeight = res * tilesize[1] minx = self.layer.bbox[0] + self.x * metaWidth - buffer[0] miny = self.layer.bbox[1] + self.y * metaHeight - buffer[1] maxx = minx + metaWidth + 2 * buffer[0] maxy = miny + metaHeight + 2 * buffer[1] return (minx, miny, maxx, maxy) class Layer (object): __slots__ = ( "name", "layers", "bbox", "data_extent", "size", "resolutions", "extension", "srs", "cache", "debug", "description", "watermarkimage", "watermarkopacity", "extent_type", "tms_type", "units", "mime_type", "paletted", "spherical_mercator", "metadata") config_properties = [ {'name':'spherical_mercator', 'description':'Layer is in spherical mercator. (Overrides bbox, maxresolution, SRS, Units)', 'type': 'boolean'}, {'name':'layers', 'description': 'Comma seperated list of layers associated with this layer.'}, {'name':'extension', 'description':'File type extension', 'default':'png'}, {'name':'bbox', 'description':'Bounding box of the layer grid', 'default':'-180,-90,180,90'}, {'name':'srs', 'description':'Spatial Reference System for the layer', 'default':'EPSG:4326'}, {'name':'data_extent', 'description':'Bounding box of the layer data. (Same SRS as the layer grid.)', 'default':"", 'type': 'map'}, ] def __init__ (self, name, layers = None, bbox = (-180, -90, 180, 90), data_extent = None, srs = "EPSG:4326", description = "", maxresolution = None, size = (256, 256), levels = 20, resolutions = None, extension = "png", mime_type = None, cache = None, debug = True, watermarkimage = None, watermarkopacity = 0.2, spherical_mercator = False, extent_type = "strict", units = "degrees", tms_type = "", **kwargs ): """Take in parameters, usually from a config file, and create a Layer. >>> l = Layer("Name", bbox="-12,17,22,36", debug="no") >>> l.bbox [-12.0, 17.0, 22.0, 36.0] >>> l.debug False >>> l = Layer("name", spherical_mercator="yes") >>> round(l.resolutions[0]) 156543.0 """ self.name = name self.description = description self.layers = layers or name self.paletted = False self.spherical_mercator = spherical_mercator and spherical_mercator.lower() in ["yes", "y", "t", "true"] if self.spherical_mercator: bbox = "-20037508.34,-20037508.34,20037508.34,20037508.34" maxresolution = "156543.0339" if srs == "EPSG:4326": srs = "EPSG:900913" units = "meters" if isinstance(bbox, str): bbox = map(float, bbox.split(",")) self.bbox = bbox if isinstance(data_extent, str): data_extent = map(float, data_extent.split(",")) self.data_extent = data_extent or bbox if isinstance(size, str): size = map(int, size.split(",")) self.size = size self.units = units self.srs = srs if extension.lower() == 'jpg': extension = 'jpeg' # MIME elif extension.lower() == 'png256': extension = 'png' self.paletted = True self.extension = extension.lower() self.mime_type = mime_type or self.format() if isinstance(debug, str): debug = debug.lower() not in ("false", "off", "no", "0") self.debug = debug self.cache = cache self.extent_type = extent_type self.tms_type = tms_type if resolutions: if isinstance(resolutions, str): resolutions = map(float,resolutions.split(",")) self.resolutions = resolutions else: maxRes = None if not maxresolution: width = bbox[2] - bbox[0] height = bbox[3] - bbox[1] if width >= height: aspect = int( float(width) / height + .5 ) # round up maxRes = float(width) / (size[0] * aspect) else: aspect = int( float(height) / width + .5 ) # round up maxRes = float(height) / (size[1] * aspect) else: maxRes = float(maxresolution) self.resolutions = [maxRes / 2 ** i for i in range(int(levels))] self.watermarkimage = watermarkimage self.watermarkopacity = float(watermarkopacity) self.metadata = {} prefix_len = len("metadata_") for key in kwargs: if key.startswith("metadata_"): self.metadata[key[prefix_len:]] = kwargs[key] def getResolution (self, (minx, miny, maxx, maxy)): """ >>> l = Layer("name") >>> l.getResolution((-180,-90,0,90)) 0.703125 """ return max( float(maxx - minx) / self.size[0], float(maxy - miny) / self.size[1] ) def getClosestLevel (self, res, size = [256, 256]): diff = sys.maxint z = None for i in range(len(self.resolutions)): if diff > abs( self.resolutions[i] - res ): diff = abs( self.resolutions[i] - res ) z = i return z def getLevel (self, res, size = [256, 256]): """ >>> l = Layer("name") >>> l.getLevel(.703125) 0 """ max_diff = res / max(size[0], size[1]) z = None for i in range(len(self.resolutions)): if abs( self.resolutions[i] - res ) < max_diff: res = self.resolutions[i] z = i break if z is None: raise TileCacheException("can't find resolution index for %f. Available resolutions are: \n%s" % (res, self.resolutions)) return z def getCell (self, (minx, miny, maxx, maxy), exact = True): """ Returns x, y, z >>> l = Layer("name") >>> l.bbox (-180, -90, 180, 90) >>> l.resolutions[0] 0.703125 >>> l.getCell((-180.,-90.,0.,90.)) (0, 0, 0) >>> l.getCell((-45.,-45.,0.,0.)) (3, 1, 2) """ res = self.getResolution((minx, miny, maxx, maxy)) x = y = None if exact: z = self.getLevel(res, self.size) else: z = self.getClosestLevel(res, self.size) res = self.resolutions[z] if exact and self.extent_type == "strict" and not self.contains((minx, miny), res): raise TileCacheException("Lower left corner (%f, %f) is outside layer bounds %s. \nTo remove this condition, set extent_type=loose in your configuration." % (minx, miny, self.bbox)) return None x0 = (minx - self.bbox[0]) / (res * self.size[0]) y0 = (miny - self.bbox[1]) / (res * self.size[1]) x = int(x0) y = int(y0) tilex = ((x * res * self.size[0]) + self.bbox[0]) tiley = ((y * res * self.size[1]) + self.bbox[1]) if exact: if (abs(minx - tilex) / res > 1): raise TileCacheException("Current x value %f is too far from tile corner x %f" % (minx, tilex)) if (abs(miny - tiley) / res > 1): raise TileCacheException("Current y value %f is too far from tile corner y %f" % (miny, tiley)) return (x, y, z) def getClosestCell (self, z, (minx, miny)): """ >>> l = Layer("name") >>> l.getClosestCell(2, (84, 17)) (6, 2, 2) """ res = self.resolutions[z] maxx = minx + self.size[0] * res maxy = miny + self.size[1] * res return self.getCell((minx, miny, maxx, maxy), False) def getTile (self, bbox): """ >>> l = Layer("name") >>> l.getTile((-180,-90,0,90)).bbox() '-180.0,-90.0,0.0,90.0' """ coord = self.getCell(bbox) if not coord: return None return Tile(self, *coord) def contains (self, (x, y), res = 0): """ >>> l = Layer("name") >>> l.contains((0,0)) True >>> l.contains((185, 94)) False """ diff_x1 = abs(x - self.bbox[0]) diff_x2 = abs(x - self.bbox[2]) diff_y1 = abs(y - self.bbox[1]) diff_y2 = abs(y - self.bbox[3]) return (x >= self.bbox[0] or diff_x1 < res) and (x <= self.bbox[2] or diff_x2 < res) \ and (y >= self.bbox[1] or diff_y1 < res) and (y <= self.bbox[3] or diff_y2 < res) def grid (self, z): """ Returns size of grid at a particular zoom level >>> l = Layer("name") >>> l.grid(3) (16.0, 8.0) """ width = (self.bbox[2] - self.bbox[0]) / (self.resolutions[z] * self.size[0]) height = (self.bbox[3] - self.bbox[1]) / (self.resolutions[z] * self.size[1]) return (width, height) def format (self): """ >>> l = Layer("name") >>> l.format() 'image/png' """ return "image/" + self.extension def renderTile (self, tile): # To be implemented by subclasses pass def render (self, tile, **kwargs): return self.renderTile(tile) class MetaLayer (Layer): __slots__ = ('metaTile', 'metaSize', 'metaBuffer') config_properties = Layer.config_properties + [ {'name':'name', 'description': 'Name of Layer'}, {'name':'metaTile', 'description': 'Should metatiling be used on this layer?', 'default': 'false', 'type':'boolean'}, {'name': 'metaSize', 'description': 'Comma seperated-pair of numbers, defininig the tiles included in a single size', 'default': "5,5"}, {'name': 'metaBuffer', 'description': 'Number of pixels outside the metatile to include in the render request.'} ] def __init__ (self, name, metatile = "", metasize = (5,5), metabuffer = (10,10), **kwargs): Layer.__init__(self, name, **kwargs) self.metaTile = metatile.lower() in ("true", "yes", "1") if isinstance(metasize, str): metasize = map(int,metasize.split(",")) if isinstance(metabuffer, str): metabuffer = map(int, metabuffer.split(",")) if len(metabuffer) == 1: metabuffer = (metabuffer[0], metabuffer[0]) self.metaSize = metasize self.metaBuffer = metabuffer def getMetaSize (self, z): if not self.metaTile: return (1,1) maxcol, maxrow = self.grid(z) return ( min(self.metaSize[0], int(maxcol + 1)), min(self.metaSize[1], int(maxrow + 1)) ) def getMetaTile (self, tile): x = int(tile.x / self.metaSize[0]) y = int(tile.y / self.metaSize[1]) return MetaTile(self, x, y, tile.z) def renderMetaTile (self, metatile, tile): import StringIO, Image data = self.renderTile(metatile) image = Image.open( StringIO.StringIO(data) ) metaCols, metaRows = self.getMetaSize(metatile.z) metaHeight = metaRows * self.size[1] + 2 * self.metaBuffer[1] for i in range(metaCols): for j in range(metaRows): minx = i * self.size[0] + self.metaBuffer[0] maxx = minx + self.size[0] ### this next calculation is because image origin is (top,left) maxy = metaHeight - (j * self.size[1] + self.metaBuffer[1]) miny = maxy - self.size[1] subimage = image.crop((minx, miny, maxx, maxy)) buffer = StringIO.StringIO() if image.info.has_key('transparency'): subimage.save(buffer, self.extension, transparency=image.info['transparency']) else: subimage.save(buffer, self.extension) buffer.seek(0) subdata = buffer.read() x = metatile.x * self.metaSize[0] + i y = metatile.y * self.metaSize[1] + j subtile = Tile( self, x, y, metatile.z ) if self.watermarkimage: subdata = self.watermark(subdata) self.cache.set( subtile, subdata ) if x == tile.x and y == tile.y: tile.data = subdata return tile.data def render (self, tile, force=False): if self.metaTile: metatile = self.getMetaTile(tile) try: self.cache.lock(metatile) image = None if not force: image = self.cache.get(tile) if not image: image = self.renderMetaTile(metatile, tile) finally: self.cache.unlock(metatile) return image else: if self.watermarkimage: return self.watermark(self.renderTile(tile)) else: return self.renderTile(tile) def watermark (self, img): import StringIO, Image, ImageEnhance tileImage = Image.open( StringIO.StringIO(img) ) wmark = Image.open(self.watermarkimage) assert self.watermarkopacity >= 0 and self.watermarkopacity <= 1 if wmark.mode != 'RGBA': wmark = wmark.convert('RGBA') else: wmark = wmark.copy() alpha = wmark.split()[3] alpha = ImageEnhance.Brightness(alpha).enhance(self.watermarkopacity) wmark.putalpha(alpha) if tileImage.mode != 'RGBA': tileImage = tileImage.convert('RGBA') watermarkedImage = Image.new('RGBA', tileImage.size, (0,0,0,0)) watermarkedImage.paste(wmark, (0,0)) watermarkedImage = Image.composite(watermarkedImage, tileImage, watermarkedImage) buffer = StringIO.StringIO() if watermarkedImage.info.has_key('transparency'): watermarkedImage.save(buffer, self.extension, transparency=compositeImage.info['transparency']) else: watermarkedImage.save(buffer, self.extension) buffer.seek(0) return buffer.read() if __name__ == "__main__": import doctest doctest.testmod() tilecache-2.11/TileCache/PaxHeader/Layers000755 777777 777777 00000000210 11456036474 022170 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311398 18 SCHILY.nlink=9 tilecache-2.11/TileCache/Layers/000755 473017214730172100000000000 11456036474 020436 5ustar00christoschristos000000 000000 tilecache-2.11/TileCache/PaxHeader/Peer000755 777777 777777 00000000210 11456036475 021625 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311433 18 SCHILY.nlink=3 tilecache-2.11/TileCache/Peer/000755 473017214730172100000000000 11456036475 020073 5ustar00christoschristos000000 000000 tilecache-2.11/TileCache/PaxHeader/Service.py000755 777777 777777 00000000210 11456032102 022740 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311431 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Service.py000755 473017214730172100000041607 11456032102 021144 0ustar00christoschristos000000 000000 #!/usr/bin/python # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors class TileCacheException(Exception): pass import sys, cgi, time, os, traceback, email, ConfigParser import Cache, Caches import Layer, Layers # Windows doesn't always do the 'working directory' check correctly. if sys.platform == 'win32': workingdir = os.path.abspath(os.path.join(os.getcwd(), os.path.dirname(sys.argv[0]))) cfgfiles = (os.path.join(workingdir, "tilecache.cfg"), os.path.join(workingdir,"..","tilecache.cfg")) else: cfgfiles = ("/etc/tilecache.cfg", os.path.join("..", "tilecache.cfg"), "tilecache.cfg") class Capabilities (object): def __init__ (self, format, data): self.format = format self.data = data class Request (object): def __init__ (self, service): self.service = service def getLayer(self, layername): try: return self.service.layers[layername] except: raise TileCacheException("The requested layer (%s) does not exist. Available layers are: \n * %s" % (layername, "\n * ".join(self.service.layers.keys()))) def import_module(name): """Helper module to import any module based on a name, and return the module.""" mod = __import__(name) components = name.split('.') for comp in components[1:]: mod = getattr(mod, comp) return mod class Service (object): __slots__ = ("layers", "cache", "metadata", "tilecache_options", "config", "files") def __init__ (self, cache, layers, metadata = {}): self.cache = cache self.layers = layers self.metadata = metadata def _loadFromSection (cls, config, section, module, **objargs): type = config.get(section, "type") for opt in config.options(section): if opt not in ["type", "module"]: objargs[opt] = config.get(section, opt) object_module = None if config.has_option(section, "module"): object_module = import_module(config.get(section, "module")) else: if module is Layer: type = type.replace("Layer", "") object_module = import_module("TileCache.Layers.%s" % type) else: type = type.replace("Cache", "") object_module = import_module("TileCache.Caches.%s" % type) if object_module == None: raise TileCacheException("Attempt to load %s failed." % type) section_object = getattr(object_module, type) if module is Layer: return section_object(section, **objargs) else: return section_object(**objargs) loadFromSection = classmethod(_loadFromSection) def _load (cls, *files): cache = None metadata = {} layers = {} config = None try: config = ConfigParser.ConfigParser() config.read(files) if config.has_section("metadata"): for key in config.options("metadata"): metadata[key] = config.get("metadata", key) if config.has_section("tilecache_options"): if 'path' in config.options("tilecache_options"): for path in config.get("tilecache_options", "path").split(","): sys.path.insert(0, path) cache = cls.loadFromSection(config, "cache", Cache) layers = {} for section in config.sections(): if section in cls.__slots__: continue layers[section] = cls.loadFromSection( config, section, Layer, cache = cache) except Exception, E: metadata['exception'] = E metadata['traceback'] = "".join(traceback.format_tb(sys.exc_traceback)) service = cls(cache, layers, metadata) service.files = files service.config = config return service load = classmethod(_load) def generate_crossdomain_xml(self): """Helper method for generating the XML content for a crossdomain.xml file, to be used to allow remote sites to access this content.""" xml = [""" """] if self.metadata.has_key('crossdomain_sites'): sites = self.metadata['crossdomain_sites'].split(',') for site in sites: xml.append(' ' % site) xml.append("") return ('text/xml', "\n".join(xml)) def renderTile (self, tile, force = False): from warnings import warn start = time.time() # do more cache checking here: SRS, width, height, layers layer = tile.layer image = None if not force: image = self.cache.get(tile) if not image: data = layer.render(tile, force=force) if (data): image = self.cache.set(tile, data) else: raise Exception("Zero length data returned from layer.") if layer.debug: sys.stderr.write( "Cache miss: %s, Tile: x: %s, y: %s, z: %s, time: %s\n" % ( tile.bbox(), tile.x, tile.y, tile.z, (time.time() - start)) ) else: if layer.debug: sys.stderr.write( "Cache hit: %s, Tile: x: %s, y: %s, z: %s, time: %s, debug: %s\n" % ( tile.bbox(), tile.x, tile.y, tile.z, (time.time() - start), layer.debug) ) return (layer.mime_type, image) def expireTile (self, tile): bbox = tile.bounds() layer = tile.layer for z in range(len(layer.resolutions)): bottomleft = layer.getClosestCell(z, bbox[0:2]) topright = layer.getClosestCell(z, bbox[2:4]) for y in range(bottomleft[1], topright[1] + 1): for x in range(bottomleft[0], topright[0] + 1): coverage = Layer.Tile(layer,x,y,z) self.cache.delete(coverage) def dispatchRequest (self, params, path_info="/", req_method="GET", host="http://example.com/"): if self.metadata.has_key('exception'): raise TileCacheException("%s\n%s" % (self.metadata['exception'], self.metadata['traceback'])) if path_info.find("crossdomain.xml") != -1: return self.generate_crossdomain_xml() if path_info.split(".")[-1] == "kml": from TileCache.Services.KML import KML return KML(self).parse(params, path_info, host) if params.has_key("scale") or params.has_key("SCALE"): from TileCache.Services.WMTS import WMTS tile = WMTS(self).parse(params, path_info, host) elif params.has_key("service") or params.has_key("SERVICE") or \ params.has_key("REQUEST") and params['REQUEST'] == "GetMap" or \ params.has_key("request") and params['request'] == "GetMap": from TileCache.Services.WMS import WMS tile = WMS(self).parse(params, path_info, host) elif params.has_key("L") or params.has_key("l") or \ params.has_key("request") and params['request'] == "metadata": from TileCache.Services.WorldWind import WorldWind tile = WorldWind(self).parse(params, path_info, host) elif params.has_key("interface"): from TileCache.Services.TileService import TileService tile = TileService(self).parse(params, path_info, host) elif params.has_key("v") and \ (params['v'] == "mgm" or params['v'] == "mgmaps"): from TileCache.Services.MGMaps import MGMaps tile = MGMaps(self).parse(params, path_info, host) elif params.has_key("tile"): from TileCache.Services.VETMS import VETMS tile = VETMS(self).parse(params, path_info, host) elif params.has_key("format") and params['format'].lower() == "json": from TileCache.Services.JSON import JSON return JSON(self).parse(params, path_info, host) else: from TileCache.Services.TMS import TMS tile = TMS(self).parse(params, path_info, host) if isinstance(tile, Layer.Tile): if req_method == 'DELETE': self.expireTile(tile) return ('text/plain', 'OK') else: return self.renderTile(tile, params.has_key('FORCE')) elif isinstance(tile, list): if req_method == 'DELETE': [self.expireTile(t) for t in tile] return ('text/plain', 'OK') else: try: import PIL.Image as Image except ImportError: raise Exception("Combining multiple layers requires Python Imaging Library.") try: import cStringIO as StringIO except ImportError: import StringIO result = None for t in tile: (format, data) = self.renderTile(t, params.has_key('FORCE')) image = Image.open(StringIO.StringIO(data)) if not result: result = image else: try: result.paste(image, None, image) except Exception, E: raise Exception("Could not combine images: Is it possible that some layers are not \n8-bit transparent images? \n(Error was: %s)" % E) buffer = StringIO.StringIO() result.save(buffer, result.format) buffer.seek(0) return (format, buffer.read()) else: return (tile.format, tile.data) def modPythonHandler (apacheReq, service): from mod_python import apache, util try: if apacheReq.headers_in.has_key("X-Forwarded-Host"): host = "http://" + apacheReq.headers_in["X-Forwarded-Host"] else: host = "http://" + apacheReq.headers_in["Host"] host += apacheReq.uri[:-len(apacheReq.path_info)] format, image = service.dispatchRequest( util.FieldStorage(apacheReq), apacheReq.path_info, apacheReq.method, host ) apacheReq.content_type = format apacheReq.status = apache.HTTP_OK if format.startswith("image/"): if service.cache.sendfile: apacheReq.headers_out['X-SendFile'] = image if service.cache.expire: apacheReq.headers_out['Expires'] = email.Utils.formatdate(time.time() + service.cache.expire, False, True) apacheReq.set_content_length(len(image)) apacheReq.send_http_header() if format.startswith("image/") and service.cache.sendfile: apacheReq.write("") else: apacheReq.write(image) except TileCacheException, E: apacheReq.content_type = "text/plain" apacheReq.status = apache.HTTP_NOT_FOUND apacheReq.send_http_header() apacheReq.write("An error occurred: %s\n" % (str(E))) except Exception, E: apacheReq.content_type = "text/plain" apacheReq.status = apache.HTTP_INTERNAL_SERVER_ERROR apacheReq.send_http_header() apacheReq.write("An error occurred: %s\n%s\n" % ( str(E), "".join(traceback.format_tb(sys.exc_traceback)))) return apache.OK def wsgiHandler (environ, start_response, service): from paste.request import parse_formvars try: path_info = host = "" if "PATH_INFO" in environ: path_info = environ["PATH_INFO"] if "HTTP_X_FORWARDED_HOST" in environ: host = "http://" + environ["HTTP_X_FORWARDED_HOST"] elif "HTTP_HOST" in environ: host = "http://" + environ["HTTP_HOST"] host += environ["SCRIPT_NAME"] req_method = environ["REQUEST_METHOD"] fields = parse_formvars(environ) format, image = service.dispatchRequest( fields, path_info, req_method, host ) headers = [('Content-Type',format)] if format.startswith("image/"): if service.cache.sendfile: headers.append(('X-SendFile', image)) if service.cache.expire: headers.append(('Expires', email.Utils.formatdate(time.time() + service.cache.expire, False, True))) start_response("200 OK", headers) if service.cache.sendfile and format.startswith("image/"): return [] else: return [image] except TileCacheException, E: start_response("404 Tile Not Found", [('Content-Type','text/plain')]) return ["An error occurred: %s" % (str(E))] except Exception, E: start_response("500 Internal Server Error", [('Content-Type','text/plain')]) return ["An error occurred: %s\n%s\n" % ( str(E), "".join(traceback.format_tb(sys.exc_traceback)))] def cgiHandler (service): try: params = {} input = cgi.FieldStorage() for key in input.keys(): params[key] = input[key].value path_info = host = "" if "PATH_INFO" in os.environ: path_info = os.environ["PATH_INFO"] if "HTTP_X_FORWARDED_HOST" in os.environ: host = "http://" + os.environ["HTTP_X_FORWARDED_HOST"] elif "HTTP_HOST" in os.environ: host = "http://" + os.environ["HTTP_HOST"] host += os.environ["SCRIPT_NAME"] req_method = os.environ["REQUEST_METHOD"] format, image = service.dispatchRequest( params, path_info, req_method, host ) print "Content-type: %s" % format if format.startswith("image/"): if service.cache.sendfile: print "X-SendFile: %s" % image if service.cache.expire: print "Expires: %s" % email.Utils.formatdate(time.time() + service.cache.expire, False, True) print "" if (not service.cache.sendfile) or (not format.startswith("image/")): if sys.platform == "win32": binaryPrint(image) else: print image except TileCacheException, E: print "Cache-Control: max-age=10, must-revalidate" # make the client reload print "Content-type: text/plain\n" print "An error occurred: %s\n" % (str(E)) except Exception, E: print "Cache-Control: max-age=10, must-revalidate" # make the client reload print "Content-type: text/plain\n" print "An error occurred: %s\n%s\n" % ( str(E), "".join(traceback.format_tb(sys.exc_traceback))) theService = {} lastRead = {} def handler (apacheReq): global theService, lastRead options = apacheReq.get_options() cfgs = cfgfiles fileChanged = False if options.has_key("TileCacheConfig"): configFile = options["TileCacheConfig"] lastRead[configFile] = time.time() cfgs = cfgs + (configFile,) try: cfgTime = os.stat(configFile)[8] fileChanged = lastRead[configFile] < cfgTime except: pass else: configFile = 'default' if not theService.has_key(configFile) or fileChanged: theService[configFile] = Service.load(*cfgs) return modPythonHandler(apacheReq, theService[configFile]) def wsgiApp (environ, start_response): global theService cfgs = cfgfiles if not theService: theService = Service.load(*cfgs) return wsgiHandler(environ, start_response, theService) def binaryPrint(binary_data): """This function is designed to work around the fact that Python in Windows does not handle binary output correctly. This function will set the output to binary, and then write to stdout directly rather than using print.""" try: import msvcrt msvcrt.setmode(sys.__stdout__.fileno(), os.O_BINARY) except: pass sys.stdout.write(binary_data) def paste_deploy_app(global_conf, full_stack=True, **app_conf): if 'tilecache_config' in app_conf: cfgfiles = (app_conf['tilecache_config'],) else: raise TileCacheException("No tilecache_config key found in configuration. Please specify location of tilecache config file in your ini file.") theService = Service.load(*cfgfiles) if 'exception' in theService.metadata: raise theService.metadata['exception'] def pdWsgiApp (environ,start_response): return wsgiHandler(environ,start_response,theService) return pdWsgiApp if __name__ == '__main__': svc = Service.load(*cfgfiles) cgiHandler(svc) tilecache-2.11/TileCache/PaxHeader/Services000755 777777 777777 00000000257 11456036475 022530 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142716 38 LIBARCHIVE.creationtime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311408 19 SCHILY.nlink=12 tilecache-2.11/TileCache/Services/000755 473017214730172100000000000 11456036475 020763 5ustar00christoschristos000000 000000 tilecache-2.11/TileCache/PaxHeader/Swarm.py000644 777777 777777 00000000210 11456032102 022426 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311406 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Swarm.py000644 473017214730172100000017604 11456032102 020632 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors import struct, time from sha import sha from bisect import bisect_left from Layer import Layer, Tile from Client import WMS from Service import Service class Message (object): types = ("PING", "PONG", "GET", "PUT", "DELETE") header = "!20sLHH" header_len = struct.calcsize(header) tilespec = "!31pHLL" tilespec_len = struct.calcsize(tilespec) def __init__ (self, msgtype, key, seq, tile = None): self.key = key self.seq_id = seq self.type = msgtype self.tile = tile def _thaw (classobj, msgtxt, layers): key, seq, msgtype, crc = struct.unpack( classobj.header, msgtxt[:classobj.header_len] ) msg = classobj( classobj.types[msgtype], key, seq ) msg.checksum = crc dispatch = getattr(msg, "thaw_" + msg.type) dispatch( msgtxt[classobj.header_len:], layers ) return msg thaw = classmethod(_thaw) def thaw_PING (self, msgtxt, layers): pass def thaw_PONG (self, msgtxt, layers): self.ping_id = struct.unpack("!L", msgtxt) def thaw_GET (self, msgtxt, layers): layername, level, row, col = struct.unpack(self.tilespec, msgtxt) self.tile = Tile(layers[layername], row, col, level) def thaw_PUT (self, msgtxt, layers): self.thaw_GET(msgtxt[:self.tilespec_len], layers) self.tile.data = msgtxt[self.tilespec_len:] def thaw_DELETE (self, msgtxt, layers): layername, level, minrow, mincol, maxrow, maxcol = \ struct.unpack("!31pHLLLL", msgtxt) self.layer = layer[layername] self.level = level self.box = (minrow, mincol, maxrow, maxcol) def freeze (self): msgtype, name = filter( lambda x: x[1] == self.type, enumerate(self.types) )[0] msgtxt = struct.pack( self.header, self.key, self.seq_id, msgtype, 0 ) dispatch = getattr(self, "freeze_" + self.type) msgtxt += dispatch() return msgtxt def freeze_PING (self): return "" def freeze_PONG (self): return struct.pack("!L", self.ping_id) def freeze_GET (self): tile = self.tile return struct.pack(self.tilespec, tile.layer.name, tile.z, tile.x, tile.y) def freeze_PUT (self): return self.freeze_GET() + self.tile.data def freeze_DELETE (self): return struct.pack("!31pHLLLL", self.tile.layer.name, self.level, *self.box) class Peer (object): min_timeout = 15 def __init__ (self, key = None, address = None, weight = 10.0): self.address = address self.key = key self.weight = float(weight) self.seq_id = 0L self.timeout = self.min_timeout class Client (Peer): max_ring_inserts = 64 replication = 3 def __init__ (self, service = None, **kwargs): Peer.__init__(self, **kwargs) self.seq_id = long( time.time() ) self.ring = [] self.peers = {} self.requests = {} self.config = service self.server = None def tile_key (self, tile): id = tile.layer + struct.pack("xHLL", tile.z, tile.x, tile.y) return sha(id).digest() def message (self, type, tile = None): self.seq_id += 1 return Message(type, self.key, self.seq_id, tile) def drop_timeout (self, peer): if peer.timeout: peer.timeout -= 1 def schedule_timeout (self, peer): self.schedule(1.0, self.drop_timeout, peer) def set_put_callback (self, tile, callback): key = self.tile_key(tile) if key not in self.requests: self.requests[key] = [] self.requests[key].append(callback) def trigger_put_callbacks (self, tile): key = self.tile_key(tile) if key not in self.requests: return for callback in self.requests[key]: callback(tile) del self.requests[key] def send (self, peer, msg): raise NotImplementedError() def schedule (self, when, callback, *args): raise NotImplementedError() def load_peers (self): raise NotImplementedError() self.schedule(15.0, self.load_peers) def load_peers_from_string(self, data): directory = [] found = {} for line in data.split("\n"): key, ip, port, weight = line.split(" ") directory.append(Peer(key, (ip, port), weight)) self.set_peers(directory) def set_peers (self, peers): new_peers = dict(map(lambda p: (p.key, p), peers)) for key, peer in new_peers: if key in self.peers: # already have it continue else: self.peers[key] = peer # don't? then add it for key, peer in self.peers: if key not in new_peers: del self.peers[key] # don't need it anymore self.rebalance_peers() def rebalance_peers (self): ring = [] peers = self.peers.values() total_weight = sum([p.weight for p in peers]) + 1.0 for peer in peers: normal_weight = peer.weight / total_weight * self.max_ring_inserts self.ring.append((peer.key, peer)) for i in range(1, int(normal_weight)): subkey = sha(peer.key + chr(i)).digest() self.ring.append((subkey, peer)) ring.sort() self.ring = ring def select_peers (self, key, count = replication): if type(key) is Tile: key = self.tile_key(key) start = bisect_left(self.ring, (key,)) cursor = start selected = [] while len(selected) < count: peer = self.ring[cursor][1] if peer.timeout and peer.key != self.key: selected.append(peer) if cursor == len(self.ring): cursor = 0 else: cursor += 1 if cursor == start: break return selected def send_GET (self, tile, callback = None): if callable(callback): self.set_put_callback(tile, callback) for target in self.select_peers(tile): msg = self.message("GET", tile) self.send(target, msg) self.schedule_timeout(target) def send_PUT (self, tile): for target in self.select_peers(tile): msg = self.message("PUT", tile) self.send(target, msg) def send_PING (self, peer): self.send( peer, self.message("PING") ) self.schedule_timeout(peer) def send_PONG (self, ping): msg = self.message("PONG") msg.ping_id = ping.seq_id peer = self.peers[ping.key] self.send( peer, msg ) def handle (self, thunk, (host, port)): msg = Message.thaw(thunk, self.config.layers) peer = self.peers[msg.key] # validate originating host/port here peer.timeout = self.max_timeout dispatch = getattr(self, "handle_" + msg.type) dispatch(peer, msg) def handle_GET (self, peer, msg): data = self.config.cache.get(msg.tile) if data: msg.tile.data = data reply = self.message("PUT", msg.tile) self.send( peer, reply ) else: self.send_PONG( msg ) def handle_PUT (self, peer, msg): self.config.cache.set(msg.tile, msg.tile.data) self.handle_put_callbacks(msg.tile) def handle_PING (self, peer, msg): self.send_PONG(msg) def handle_PONG (self, peer, msg): # already reset peer timeout, nothing to be done pass def handle_DELETE (self, peer, msg): # not implemented yet pass tilecache-2.11/TileCache/Services/PaxHeader/__init__.py000644 777777 777777 00000000210 10725437107 024673 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311414 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/__init__.py000644 473017214730172100000000000 10725437107 023055 0ustar00christoschristos000000 000000 tilecache-2.11/TileCache/Services/PaxHeader/JSON.py000644 777777 777777 00000000210 11027761617 023707 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311409 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/JSON.py000644 473017214730172100000002264 11027761617 022107 0ustar00christoschristos000000 000000 from TileCache.Services.TMS import TMS from TileCache.Service import Request, Capabilities import simplejson class JSON(TMS): def parse(self, fields, path, host): layers = {} type = "object" if fields.has_key("type") and fields['type'] == "list": layers = [] type = "list" match_srs = False if 'srs' in fields: match_srs = fields['srs'] for name, layer in self.service.layers.items(): if match_srs and layer.srs != match_srs: continue data = { 'bbox': layer.bbox, 'data_extent': layer.data_extent, 'resolutions': layer.resolutions, 'metadata': layer.metadata, 'srs': layer.srs, 'units': layer.units, 'name': name, } if type == "list": layers.append(data) else: layers[name] = data obj = {'layers': layers} data = simplejson.dumps(obj) if 'callback' in fields: data = "%s(%s)" % (fields['callback'], data) return ("application/json", data) tilecache-2.11/TileCache/Services/PaxHeader/KML.py000644 777777 777777 00000000210 11456032102 023543 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311415 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/KML.py000644 473017214730172100000005375 11456032102 021751 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Service import Request, Capabilities from TileCache.Services.TMS import TMS import TileCache.Layer as Layer class KML(TMS): def parse (self, fields, path, host): tile = TMS.parse(self,fields, path, host) kml = self.generate_kml_doc(tile, base_path=host) return ("application/vnd.google-earth.kml+xml", kml) def generate_kml_doc(self, tile, base_path="", include_wrapper = True): tiles = [ Layer.Tile(tile.layer, tile.x << 1, tile.y << 1, tile.z + 1), Layer.Tile(tile.layer, (tile.x << 1) + 1, tile.y << 1, tile.z + 1), Layer.Tile(tile.layer, (tile.x << 1) + 1, (tile.y << 1) + 1, tile.z + 1), Layer.Tile(tile.layer, tile.x << 1 , (tile.y << 1) + 1, tile.z + 1) ] network_links = [] for single_tile in tiles: if single_tile.z >= len(tile.layer.resolutions): continue b = single_tile.bounds() network_links.append(""" tile 256-1 %s%s %s%s %s/1.0.0/%s/%s/%s/%s.kml onRegion """ % (b[3], b[1], b[2], b[0], base_path, single_tile.layer.name, single_tile.z, single_tile.x, single_tile.y)) b = tile.bounds() kml = [] if include_wrapper: kml.append( """ """) if tile.z == len(tile.layer.resolutions) - 1: max_lod_pixels = -1 else: max_lod_pixels = 512 kml.append(""" 256%d %s%s %s%s %s %s/1.0.0/%s/%s/%s/%s %s%s %s%s %s """ % (max_lod_pixels, b[3], b[1], b[2], b[0], tile.z, base_path, tile.layer.name, tile.z, tile.x, tile.y, b[3], b[1], b[2], b[0], "\n".join(network_links))) if include_wrapper: kml.append("""""" ) kml = "\n".join(kml) return kml tilecache-2.11/TileCache/Services/PaxHeader/MGMaps.py000644 777777 777777 00000000210 11456032102 024244 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311417 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/MGMaps.py000644 473017214730172100000001771 11456032102 022446 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Service import Request, Capabilities import TileCache.Layer as Layer class MGMaps (Request): def parse (self, fields, path, host): param = {} for key in ['layer', 'zoom', 'x', 'y']: if fields.has_key(key.upper()): param[key] = fields[key.upper()] elif fields.has_key(key): param[key] = fields[key] else: param[key] = "" return self.getMap(param) def getMap (self, param): layer = self.getLayer(param["layer"]) level = int(param["zoom"]) level = 17 - level x = float(param["x"]) y = float(param["y"]) res = layer.resolutions[level] maxY = int( round( (layer.bbox[3] - layer.bbox[1]) / (res * layer.size[1]) ) ) - 1 tile = Layer.Tile(layer, x, maxY - y, level) return tile tilecache-2.11/TileCache/Services/PaxHeader/TileService.py000644 777777 777777 00000000210 11456032102 025336 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311412 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/TileService.py000644 473017214730172100000001514 11456032102 023533 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Service import Request, Capabilities import TileCache.Layer as Layer class TileService (Request): def parse (self, fields, path, host): param = {} for key in ['interface', 'version', 'dataset', 'level', 'x', 'y', 'request']: if fields.has_key(key.upper()): param[key] = fields[key.upper()] elif fields.has_key(key): param[key] = fields[key] else: param[key] = "" return self.getMap(param) def getMap (self, param): layer = self.getLayer(param["dataset"]) level = int(param["level"]) y = float(param["y"]) x = float(param["x"]) tile = Layer.Tile(layer, x, y, level) return tile tilecache-2.11/TileCache/Services/PaxHeader/TMS.py000644 777777 777777 00000000210 11456032102 023563 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311416 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/TMS.py000644 473017214730172100000007135 11456032102 021765 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Service import Request, Capabilities import TileCache.Layer as Layer class TMS (Request): def parse (self, fields, path, host): # /1.0.0/global_mosaic/0/0/0.jpg parts = filter( lambda x: x != "", path.split("/") ) if not host[-1] == "/": host = host + "/" if len(parts) < 1: return self.serverCapabilities(host) elif len(parts) < 2: return self.serviceCapabilities(host, self.service.layers) else: layer = self.getLayer(parts[1]) if len(parts) < 3: return self.layerCapabilities(host, layer) else: parts[-1] = parts[-1].split(".")[0] tile = None if layer.tms_type == "google" or (fields.has_key('type') and fields['type'] == 'google'): res = layer.resolutions[int(parts[2])] maxY = int( round( (layer.bbox[3] - layer.bbox[1]) / (res * layer.size[1]) ) ) - 1 tile = Layer.Tile(layer, int(parts[3]), maxY - int(parts[4]), int(parts[2])) else: tile = Layer.Tile(layer, int(parts[3]), int(parts[4]), int(parts[2])) return tile def serverCapabilities (self, host): return Capabilities("text/xml", """ """ % host) def serviceCapabilities (self, host, layers): xml = """ """ for name, layer in layers.items(): profile = "none" if (layer.srs == "EPSG:4326"): profile = "global-geodetic" elif (layer.srs == "OSGEO:41001"): profile = "global-mercator" xml += """ """ % (host, name, layer.srs, layer.name, profile) xml += """ """ return Capabilities("text/xml", xml) def layerCapabilities (self, host, layer): tms_type = layer.tms_type or "default" xml = """ %s %s %s """ % (host, tms_type, layer.name, layer.description, layer.srs, layer.bbox[0], layer.bbox[1], layer.bbox[2], layer.bbox[3], layer.bbox[0], layer.bbox[1], layer.size[0], layer.size[1], layer.format(), layer.extension) for z, res in enumerate(layer.resolutions): xml += """ """ % ( host, layer.name, z, res, z) xml += """ """ return Capabilities("text/xml", xml) tilecache-2.11/TileCache/Services/PaxHeader/VETMS.py000644 777777 777777 00000000210 11456032102 024016 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311413 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/VETMS.py000644 473017214730172100000003562 11456032102 022220 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Service import Request, Capabilities import TileCache.Layer as Layer class VETMS (Request): """ Support for Virtual Earth quadkey-based URLs. ?ve=true&layer=global_mosaic&tile=000.jpg """ def parse (self, fields, path, host): """Take in VETMS params and return a tile.""" for key in ['layer', 'tile']: if fields.has_key(key.upper()): fields[key] = fields[key.upper()] elif not fields.has_key(key): fields[key] = "" layer = self.getLayer(fields['layer']) tilenumber = str(fields['tile']) quadkey = tilenumber.split(".")[0] tile = None cell = self.unquad(quadkey) tile = Layer.Tile(layer, cell[0], cell[1], cell[2]) return tile def unquad (self, quad): """ Returns x/y/z ints based on a quadkey. >>> ve = VETMS({}) >>> ve.unquad("1") [1, 1, 1] >>> ve.unquad("") [0, 0, 0] >>> ve.unquad("02") [0, 2, 2] """ z = len(quad) col = int(0) row = int(pow(2, z)-1) quadint = int(0) for i in range (0, z): quadint = int(quad[i]) tmp = int(pow(2, z-(i+1))) if (quadint == 1): col += tmp elif (quadint == 2): row -= tmp elif (quadint == 3): col += tmp row -= tmp cell = [int(col), int(row), int(z)] return cell def serverCapabilities (self, host): """Report capabilities for VETMS.""" return Capabilities("text/xml", """ """ % host) tilecache-2.11/TileCache/Services/PaxHeader/WMS.py000644 777777 777777 00000000210 11456032102 023566 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311418 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/WMS.py000644 473017214730172100000012321 11456032102 021761 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Service import Request, Capabilities import TileCache.Layer as Layer class WMS (Request): def parse (self, fields, path, host): param = {} for key in ['bbox', 'layers', 'request', 'version']: if fields.has_key(key.upper()): param[key] = fields[key.upper()] elif fields.has_key(key): param[key] = fields[key] else: param[key] = "" if param["request"] == "GetCapabilities": return self.getCapabilities(host + path, param) else: return self.getMap(param) def getMap (self, param): bbox = map(float, param["bbox"].split(",")) layers = param["layers"].split(",") tiles = [] for name in layers: tile = self.getLayer(name).getTile(bbox) if not tile: raise Exception( "couldn't calculate tile index for layer %s from (%s)" % (layer.name, bbox)) tiles.append(tile) if len(tiles) > 1: return tiles else: return tiles[0] def getCapabilities (self, host, param): if host[-1] not in "?&": if "?" in host: host += "&" else: host += "?" metadata = self.service.metadata if "description" in metadata: description = metadata["description"] else: description = "" formats = {} for layer in self.service.layers.values(): formats[layer.format()] = 1 formats = formats.keys() xml = """ ]> OGC:WMS %s """ % (description, host) xml += """ application/vnd.ogc.wms_xml """ % (host) xml += """ """ for format in formats: xml += """ %s\n""" % format xml += """ """ % (host) xml += """ text/plain """ for name, layer in self.service.layers.items(): resolutions = " ".join(["%.20f" % r for r in layer.resolutions]) xml += """ %s %s %d %d %s %s """ % ( layer.srs, layer.srs, layer.bbox[0], layer.bbox[1], layer.bbox[2], layer.bbox[3], resolutions, layer.size[0], layer.size[1], layer.format(), name ) xml += """ TileCache Layers""" for name, layer in self.service.layers.items(): xml += """ %s %s %s """ % ( name, layer.name, layer.srs, layer.srs, layer.bbox[0], layer.bbox[1], layer.bbox[2], layer.bbox[3]) xml += """ """ return Capabilities("text/xml", xml) tilecache-2.11/TileCache/Services/PaxHeader/WMTS.py000644 777777 777777 00000000210 11456032102 023712 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311411 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/WMTS.py000644 473017214730172100000002313 11456032102 022105 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Service import Request, Capabilities, TileCacheException import TileCache.Layer as Layer class WMTS (Request): meters_per_unit = { 'degrees': 111118.752, 'meters': 1, 'feet': 0.3048 } def parse (self, fields, path, host): for key in ['scale','layer','tilerow','tilecol']: if fields.has_key(key.upper()): fields[key] = fields[key.upper()] elif not fields.has_key(key): fields[key] = "" layer = self.getLayer(fields['layer']) if not layer.units: raise TileCacheException("No units were specified on the layer. WMTS support requires units to be defined for the layer.") res = .00028 * float(fields['scale']) / self.meters_per_unit[layer.units] z = layer.getLevel(res, layer.size) tile = None maxY = int( round( (layer.bbox[3] - layer.bbox[1]) / (res * layer.size[1]) ) ) - 1 tile = Layer.Tile(layer, int(fields['tilecol']), maxY - int(fields['tilerow']), z) return tile tilecache-2.11/TileCache/Services/PaxHeader/WorldWind.py000644 777777 777777 00000000210 11456032102 025031 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311410 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Services/WorldWind.py000644 473017214730172100000006656 11456032102 023242 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Service import Request, Capabilities import TileCache.Layer as Layer class WorldWind (Request): def parse (self, fields, path, host): param = {} for key in ['t', 'l', 'x', 'y', 'request']: if fields.has_key(key.upper()): param[key] = fields[key.upper()] elif fields.has_key(key): param[key] = fields[key] else: param[key] = "" if param["request"] == "GetCapabilities" or param["request"] == "metadata": return self.getCapabilities(host + path, param) else: return self.getMap(param) def getMap (self, param): layer = self.getLayer(param["t"]) level = int(param["l"]) y = float(param["y"]) x = float(param["x"]) tile = Layer.Tile(layer, x, y, level) return tile def getCapabilities (self, host, param): metadata = self.service.metadata if "description" in metadata: description = metadata["description"] else: description = "" formats = {} for layer in self.service.layers.values(): formats[layer.format()] = 1 formats = formats.keys() xml = """ """ for name, layer in self.service.layers.items(): if (layer.srs != "EPSG:4326"): continue xml += """ %s Layer: %s 0 %s %s %s %s false %s %s %s %s %s %s SRS:%s """ % (name, name, layer.description, float(layer.bbox[0]), float(layer.bbox[1]), float(layer.bbox[2]), float(layer.bbox[3]), layer.resolutions[0] * layer.size[0], len(layer.resolutions), layer.size[0], layer.extension, host, name, layer.srs) xml += """ """ return Capabilities("text/xml", xml) tilecache-2.11/TileCache/Peer/PaxHeader/Twisted.py000644 777777 777777 00000000210 11456032102 023653 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311434 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Peer/Twisted.py000644 473017214730172100000002103 11456032102 022043 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from Swarm import Message, Client from twisted.internet.protocol import DatagramProtocol from twisted.web import getPage from twisted.internet import reactor class TwistedClient (DatagramProtocol, Client): directoryURL = "..." def __init__ (self, service, key, weight = 10.0, *args, **kwargs): DatagramProtocol.__init__(self, *args, **kwargs) Client.__init__(self, service, key, None, weight) def datagramReceived(self, data, (host, port)): self.handle(data, (host, port)) def send (self, peer, msg): self.transport.write( msg.freeze(), peer.address ) def schedule (self, when, callback, *args): reactor.callLater(time.time() + when, callback, *args) def load_peers (self): d = getPage(self.directoryURL) d.addCallback(self.load_peers_from_string) self.schedule(15.0, self.load_peers) def startProtocol (self): self.load_peers() if __name__ == '__main__': reactor.listenUDP(5150, Echo()) reactor.run() tilecache-2.11/TileCache/Layers/PaxHeader/__init__.py000644 777777 777777 00000000210 10725446217 024351 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311400 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Layers/__init__.py000644 473017214730172100000000000 10725446217 022533 0ustar00christoschristos000000 000000 tilecache-2.11/TileCache/Layers/PaxHeader/ArcXML.py000644 777777 777777 00000000210 11456032102 023662 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311403 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Layers/ArcXML.py000644 473017214730172100000011345 11456032102 022062 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors """ ArcXML support. This layer will make requests to an ArcIMS server. """ from TileCache.Layer import MetaLayer from TileCache.Service import TileCacheException import urllib import xml.dom.minidom as m class ArcXML(MetaLayer): config_properties = [ {'name':'name', 'description': 'Name of Layer'}, {'name':'url', 'description': 'URL of Remote Layer'}, {'name':'layers', 'description': 'Comma seperated list of layers associated with this layer.'}, {'name':'off_layers', 'description': 'Comma-seperated layers to turn on'}, {'name':'projection', 'description': 'WKT String, or, if the string starts with "@", a file containing a WKT string.'} ] + MetaLayer.config_properties def __init__ (self, name, url = None, off_layers = "", projection = None, **kwargs): """ Accepts projection in one of two forms: * Raw string * String beginning with @, in which case string is treated as filename """ MetaLayer.__init__(self, name, **kwargs) self.url = url self.off_layers = off_layers self.projection = None if projection is not None: if projection.startswith("@"): self.projection = open(projection[1:]).read() else: self.projection = projection def gen_xml(self, tile): """ >>> from TileCache.Layer import Tile >>> l = ArcXML("foo", projection="fooproj") >>> xml = l.gen_xml(Tile(l, 0,0,0)) >>> print xml.replace("\\n", "") >>> doc = m.parseString(xml) >>> feat_coord_sys = doc.getElementsByTagName("FEATURECOORDSYS") >>> len(feat_coord_sys) 1 >>> feat_coord_sys[0].getAttribute("string") u'fooproj' >>> l = ArcXML("foo") >>> xml = l.gen_xml(Tile(l, 0,0,0)) >>> doc = m.parseString(xml) >>> feat_coord_sys = doc.getElementsByTagName("FEATURECOORDSYS") >>> len(feat_coord_sys) 0 >>> import os >>> f = open('tmp_tc_test_file', 'w') >>> f.write('foo<>"][') >>> f.close() >>> l = ArcXML("foo", projection="@tmp_tc_test_file") >>> xml = l.gen_xml(Tile(l, 0,0,0)) >>> doc = m.parseString(xml) >>> feat_coord_sys = doc.getElementsByTagName("FEATURECOORDSYS") >>> feat_coord_sys[0].toxml() u'' >>> os.unlink("tmp_tc_test_file") """ layers = [] off_layers = [] for layer_id in self.layers.split(","): if layer_id.strip(): layers.append('' % layer_id) for layer_id in self.off_layers.split(","): if layer_id.strip(): off_layers.append('' % layer_id) bbox = tile.bounds() projection_text = "" if self.projection: doc = m.Document() feat = doc.createElement("FEATURECOORDSYS") feat.setAttribute("string", self.projection) projection_text = "%s\n%s" % ( feat.toxml(), feat.toxml().replace("FEATURE", "FILTER")) return """ %s %s %s """ % (bbox[0], bbox[1], bbox[2], bbox[3], projection_text, tile.size()[0], tile.size()[1], "\n".join(layers), "\n".join(off_layers)) def renderTile(self, tile): xml = self.gen_xml(tile) try: xmldata = urllib.urlopen(self.url, xml).read() except Exception, error: raise TileCacheException("Error fetching URL. Exception was: %s\n Input XML:\n %s " % (error, xml)) try: doc = m.parseString(xmldata) img_url = doc.getElementsByTagName("OUTPUT")[0].attributes['url'].value except Exception, error: raise TileCacheException("Error fetching URL. Exception was: %s\n Output XML: \n%s\n\nInput XML:\n %s " % (error, xmldata, xml)) tile.data = urllib.urlopen(img_url).read() return tile.data tilecache-2.11/TileCache/Layers/PaxHeader/GDAL.py000644 777777 777777 00000000210 11456032102 023303 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311405 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Layers/GDAL.py000644 473017214730172100000012741 11456032102 021504 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Layer import MetaLayer import osgeo.gdal as gdal import osgeo.gdal_array as gdalarray import numpy import PIL class GDAL(MetaLayer): """ The GDAL Layer allows you to set up any GDAL datasource in TileCache. Areas not covered by the image will be transparent in formats which support transparency. The GDAL transparency is maintained. All bands of an image are read from the source file at this time. This Layer does not support images where north is not up. Special effort is taken when the GeoTransform on the image is the default (0.0, 1.0, 0.0, 0.0, 0.0, 1.0): In that case, the geotransform is replaced with (0.0, 1.0, 0.0, self.ds.RasterYSize, 0.0, -1.0) . This allows one to use the GDAL layer with non-georeferenced images: Simply specify a bbox=0,0,size_x,size_y, and then you can use the image in TileCache. This is likely a better idea than using the Image layer, if you can install GDAL, since GDAL may be more efficient in managing subsetting of files, especially geographic sized ones, due to its ability to support overviews on files it is reading. This layer depends on: * GDAL 1.5 with Python Bindings * PIL * numpy """ config_properties = [ {'name':'name', 'description': 'Name of Layer'}, {'name':'file', 'description': 'GDAL-readable file path.'}, ] + MetaLayer.config_properties def __init__ (self, name, file = None, **kwargs): MetaLayer.__init__(self, name, **kwargs) self.ds = gdal.Open(file) self.geo_transform = self.ds.GetGeoTransform() if self.geo_transform[2] != 0 or self.geo_transform[4] != 0: raise Exception("Image is not 'north-up', can not use.") if self.geo_transform == (0.0, 1.0, 0.0, 0.0, 0.0, 1.0): self.geo_transform = (0.0, 1.0, 0.0, self.ds.RasterYSize, 0.0, -1.0) size = [self.ds.RasterXSize, self.ds.RasterYSize] xform = self.geo_transform self.data_extent = [ xform[0] + self.ds.RasterYSize * xform[2], xform[3] + self.ds.RasterYSize * xform[5], xform[0] + self.ds.RasterXSize * xform[1], xform[3] + self.ds.RasterXSize * xform[4] ] def renderTile(self, tile): import PIL.Image as PILImage import StringIO bounds = tile.bounds() im = None # If the image is entirely outside the bounds, don't bother doing anything with it: # just return an 'empty' tile. if not (bounds[2] < self.data_extent[0] or bounds[0] > self.data_extent[2] or bounds[3] < self.data_extent[1] or bounds[1] > self.data_extent[3]): tile_offset_left = tile_offset_top = 0 target_size = tile.size() off_x = int((bounds[0] - self.geo_transform[0]) / self.geo_transform[1]); off_y = int((bounds[3] - self.geo_transform[3]) / self.geo_transform[5]); width_x = int(((bounds[2] - self.geo_transform[0]) / self.geo_transform[1]) - off_x); width_y = int(((bounds[1] - self.geo_transform[3]) / self.geo_transform[5]) - off_y); # Prevent from reading off the sides of an image if off_x + width_x > self.ds.RasterXSize: oversize_right = off_x + width_x - self.ds.RasterXSize target_size = [ target_size[0] - int(float(oversize_right) / width_x * target_size[0]), target_size[1] ] width_x = self.ds.RasterXSize - off_x if off_x < 0: oversize_left = -off_x tile_offset_left = int(float(oversize_left) / width_x * target_size[0]) target_size = [ target_size[0] - int(float(oversize_left) / width_x * target_size[0]), target_size[1], ] width_x = width_x + off_x off_x = 0 if off_y + width_y > self.ds.RasterYSize: oversize_bottom = off_y + width_y - self.ds.RasterYSize target_size = [ target_size[0], target_size[1] - round(float(oversize_bottom) / width_y * target_size[1]) ] width_y = self.ds.RasterYSize - off_y if off_y < 0: oversize_top = -off_y tile_offset_top = int(float(oversize_top) / width_y * target_size[1]) target_size = [ target_size[0], target_size[1] - int(float(oversize_top) / width_y * target_size[1]), ] width_y = width_y + off_y off_y = 0 bands = self.ds.RasterCount array = numpy.zeros((target_size[1], target_size[0], bands), numpy.uint8) for i in range(bands): array[:,:,i] = gdalarray.BandReadAsArray(self.ds.GetRasterBand(i+1), off_x, off_y, width_x, width_y, target_size[0], target_size[1]) im = PIL.Image.fromarray(array) big = PIL.Image.new("RGBA", tile.size(), (0,0,0,0)) if im: big.paste(im, (tile_offset_left, tile_offset_top)) buffer = StringIO.StringIO() big.save(buffer, self.extension) buffer.seek(0) tile.data = buffer.read() return tile.data tilecache-2.11/TileCache/Layers/PaxHeader/Image.py000644 777777 777777 00000000210 11456032102 023616 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311401 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Layers/Image.py000644 473017214730172100000005751 11456032102 022022 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Layer import MetaLayer class Image(MetaLayer): """The ImageLayer allows you to set up any image file in TileCache. All you need is an image, and a geographic bounds (filebounds), Which is passed in as a single, comma seperated string in the form minx,miny,maxx,maxy.""" config_properties = [ {'name':'name', 'description': 'Name of Layer'}, {'name':'file', 'description': 'Location of PIL-readable file.'}, ] + MetaLayer.config_properties def __init__ (self, name, file = None, filebounds = "-180,-90,180,90", transparency = False, scaling = "nearest", **kwargs): import PIL.Image as PILImage MetaLayer.__init__(self, name, **kwargs) self.file = file self.filebounds = map(float,filebounds.split(",")) self.image = PILImage.open(self.file) self.image_size = self.image.size self.image_res = [(self.filebounds[2] - self.filebounds[0]) / self.image_size[0], (self.filebounds[3] - self.filebounds[1]) / self.image_size[1] ] self.scaling = scaling.lower() if isinstance(transparency, str): transparency = transparency.lower() in ("true", "yes", "1") self.transparency = transparency def renderTile(self, tile): import PIL.Image as PILImage import StringIO bounds = tile.bounds() size = tile.size() min_x = (bounds[0] - self.filebounds[0]) / self.image_res[0] min_y = (self.filebounds[3] - bounds[3]) / self.image_res[1] max_x = (bounds[2] - self.filebounds[0]) / self.image_res[0] max_y = (self.filebounds[3] - bounds[1]) / self.image_res[1] if self.scaling == "bilinear": scaling = PILImage.BILINEAR elif self.scaling == "bicubic": scaling = PILImage.BICUBIC elif self.scaling == "antialias": scaling = PILImage.ANTIALIAS else: scaling = PILImage.NEAREST crop_size = (max_x-min_x, max_y-min_y) if min(min_x, min_y, max_x, max_y) < 0: if self.transparency and self.image.mode in ("L", "RGB"): self.image.putalpha(PILImage.new("L", self.image_size, 255)); sub = self.image.transform(crop_size, PILImage.EXTENT, (min_x, min_y, max_x, max_y)) else: sub = self.image.crop((min_x, min_y, max_x, max_y)); if crop_size[0] < size[0] and crop_size[1] < size[1] and self.scaling == "antialias": scaling = PILImage.BICUBIC sub = sub.resize(size, scaling) buffer = StringIO.StringIO() if self.image.info.has_key('transparency'): sub.save(buffer, self.extension, transparency=self.image.info['transparency']) else: sub.save(buffer, self.extension) buffer.seek(0) tile.data = buffer.read() return tile.data tilecache-2.11/TileCache/Layers/PaxHeader/Mapnik.py000644 777777 777777 00000000210 11456032102 024013 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311399 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Layers/Mapnik.py000644 473017214730172100000006323 11456032102 022213 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors import sys from TileCache.Layer import MetaLayer class Mapnik(MetaLayer): config_properties = [ {'name':'name', 'description': 'Name of Layer'}, {'name':'mapfile', 'description': 'Location of Mapnik XML map description.'}, {'name':'projection', 'description': 'Target map projection.'}, {'name':'fonts', 'description': 'Comma-seperated list of fonts to add to the Mapik registered fonts list.'}, ] + MetaLayer.config_properties def __init__ (self, name, mapfile = None, projection = None, fonts = None, **kwargs): MetaLayer.__init__(self, name, **kwargs) self.mapfile = mapfile self.mapnik = None self.projection = projection if fonts: self.fonts = fonts.split(",") else: self.fonts = [] def renderTile(self, tile): import mapnik if self.mapnik: m = self.mapnik else: if self.fonts: engine = mapnik.FontEngine.instance() for font in self.fonts: engine.register_font(font) # Init it as 0,0 m = mapnik.Map( 0, 0 ) mapnik.load_map(m,self.mapfile) if self.projection: m.srs = self.projection # Restrict layer list, if requested if self.layers and self.layers != self.name: layers = self.layers.split(",") for layer_num in range(len(m.layers)-1, -1, -1): l = m.layers[layer_num] if l.name not in layers: del m.layers[layer_num] if self.debug: print >>sys.stderr, "Removed layer %s loaded from %s, not in list: %s" % (l.name, self.mapfile, layers) # this will insure that it gets cached in mod_python self.mapnik = m # Set the mapnik size to match the size of the current tile m.width = tile.size()[0] m.height = tile.size()[1] bbox = tile.bounds() bbox = mapnik.Envelope(bbox[0], bbox[1], bbox[2], bbox[3]) m.zoom_to_box(bbox) im = mapnik.Image( *tile.size() ) mapnik.render(m, im) if hasattr(im, 'tostring'): if self.paletted: data = im.tostring('png256') else: data = im.tostring(self.extension) tile.data = data return tile.data elif hasattr(mapnik, 'rawdata'): data = mapnik.rawdata(im) import PIL.Image, StringIO im = PIL.Image.fromstring('RGBA', tile.size(), data) buffer = StringIO.StringIO() if self.paletted: print >>sys.stderr, "Mapnik's 8-bit (png256) format not supported with PIL" im.save(buffer, self.extension) buffer.seek(0) tile.data = buffer.read() return tile.data else: raise Exception("Something is wrong: your version of Mapnik can't create a string from an image.") tilecache-2.11/TileCache/Layers/PaxHeader/MapServer.py000644 777777 777777 00000000210 11456032102 024500 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311402 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Layers/MapServer.py000644 473017214730172100000004111 11456032102 022671 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Layer import MetaLayer class MapServer(MetaLayer): config_properties = [ {'name':'name', 'description': 'Name of Layer'}, {'name':'mapfile', 'description': 'Location of MapServer map file.'}, ] + MetaLayer.config_properties def __init__ (self, name, mapfile = None, styles = "", **kwargs): MetaLayer.__init__(self, name, **kwargs) self.mapfile = mapfile self.styles = styles def get_map(self, tile): # tile is unused here but might be used in a subclass # where the mapfile config depends on the tile extents or layer import mapscript wms = mapscript.mapObj(self.mapfile) if self.metaBuffer: try: # if the metadata is already set, don't override. wms.getMetaData("labelcache_map_edge_buffer") except mapscript._mapscript.MapServerError: # We stick an extra buffer of 5px in there because in the case # of shields, we want to account for when the shield could get # cut even though the label that the shield is on isn't. buffer = -max(self.metaBuffer[0], self.metaBuffer[1]) - 5 wms.setMetaData("labelcache_map_edge_buffer", str(buffer)) return wms def get_request(self, tile): import mapscript req = mapscript.OWSRequest() req.setParameter("bbox", tile.bbox()) req.setParameter("width", str(tile.size()[0])) req.setParameter("height", str(tile.size()[1])) req.setParameter("srs", self.srs) req.setParameter("format", self.mime_type) req.setParameter("layers", self.layers) req.setParameter("styles", self.styles) req.setParameter("request", "GetMap") return req def renderTile(self, tile): wms = self.get_map(tile) req = self.get_request(tile) wms.loadOWSParameters(req) mapImage = wms.draw() tile.data = mapImage.getBytes() return tile.data tilecache-2.11/TileCache/Layers/PaxHeader/WMS.py000644 777777 777777 00000000210 11456032102 023242 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142716 20 atime=1287142716 24 SCHILY.dev=234881029 22 SCHILY.ino=3311404 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Layers/WMS.py000644 473017214730172100000002232 11456032102 021435 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Layer import MetaLayer import TileCache.Client as WMSClient class WMS(MetaLayer): config_properties = [ {'name':'name', 'description': 'Name of Layer'}, {'name':'url', 'description': 'URL of Remote Layer'}, {'name':'user', 'description': 'Username of remote server: used for basic-auth protected backend WMS layers.'}, {'name':'password', 'description': 'Password of remote server: Use for basic-auth protected backend WMS layers.'}, ] + MetaLayer.config_properties def __init__ (self, name, url = None, user = None, password = None, **kwargs): MetaLayer.__init__(self, name, **kwargs) self.url = url self.user = user self.password = password def renderTile(self, tile): wms = WMSClient.WMS( self.url, { "bbox": tile.bbox(), "width": tile.size()[0], "height": tile.size()[1], "srs": self.srs, "format": self.mime_type, "layers": self.layers, }, self.user, self.password) tile.data, response = wms.fetch() return tile.data tilecache-2.11/TileCache/Caches/PaxHeader/__init__.py000644 777777 777777 00000000210 10725446355 024303 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311425 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Caches/__init__.py000644 473017214730172100000000000 10725446355 022465 0ustar00christoschristos000000 000000 tilecache-2.11/TileCache/Caches/PaxHeader/AWSS3.py000644 777777 777777 00000000210 11456032102 023363 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311426 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Caches/AWSS3.py000644 473017214730172100000014417 11456032102 021566 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Cache import Cache import time class AWSS3(Cache): import_error = "Problem importing S3 support library. You must have either boto or the Amazon S3 library.\nErrors:\n * %s" def __init__ (self, access_key, secret_access_key, use_tms_paths = "False", **kwargs): self.module = None try: import boto.s3 self.s3 = boto.s3 self.module = "boto" except ImportError, E: exceptions = [str(E)] try: import S3 self.s3 = S3 self.module = "amazon" except Exception, E: exceptions.append(str(E)) raise Exception(self.import_error % ('\n * '.join(exceptions))) Cache.__init__(self, **kwargs) self.bucket_name = "%s-tilecache" % access_key.lower() if use_tms_paths.lower() in ("true", "yes", "1"): use_tms_paths = True elif use_tms_paths.lower() == "flipped": use_tms_paths = "google" self.use_tms_paths = use_tms_paths if self.module == "amazon": self.cache = self.s3.AWSAuthConnection(access_key, secret_access_key) self.cache.create_bucket(self.bucket_name) else: self.cache = self.s3.connection.S3Connection(access_key, secret_access_key) self.bucket = self.cache.create_bucket(self.bucket_name) def getBotoKey(self, key): boto_key = self.s3.key.Key(self.bucket) boto_key.key = key return boto_key def getKey(self, tile): if self.use_tms_paths == True or self.use_tms_paths == "flipped": grid = tile.layer.grid(tile.z) y = tile.y if self.use_tms_paths == "flipped": y = int(grid[1] - 1 - tile.y) version = "1.0.0" path = "/".join(map(str, [version, tile.layer.name, tile.z, tile.x, y])) path = ".".join(map(str, [path, tile.layer.extension])) else: path = "-".join(map(str, [tile.layer.name, tile.z , tile.x, tile.y])) return path def get(self, tile): key = self.getKey(tile) tile.data = self.getObject(key) return tile.data def getObject(self, key): data = None if self.module == "amazon": response = self.cache.get(self.bucket_name, key) if not response.object.data.startswith(" or ,, delete_tiles""") parser.add_option('-z', dest='zoom', help='zoom level for count_tiles (requires layer name)') parser.add_option('-l', dest='layer', help='layer name for count_tiles') parser.add_option('-k', dest='key', help='access key for S3') parser.add_option('-s', dest='secret', help='secret access key for S3') (options, args) = parser.parse_args() if not options.key or not options.secret or not args: parser.print_help() sys.exit() def create_prefix(options): prefix = "" if options.layer: prefix = "%s-" % options.layer if options.zoom: prefix = "%s%s-" % (prefix, options.zoom) return prefix # Debug mode. a = AWSS3(options.key, options.secret) if args[0] == "list_locks": print ','.join(a.keys({'prefix':'lock-'})) elif args[0] == "list_keys": print ','.join(a.keys()) elif args[0] == "count_tiles" or args[0] == "show_tiles": opts = { 'prefix': create_prefix(options) } if args[0] == "show_tiles": print ",".join(a.keys(opts)) else: print len(a.keys(opts)) elif args[0] == "delete": for key in args[1].split(","): a.deleteObject(key) elif args[0] == "delete_tiles": opts = { 'prefix': create_prefix(options) } keys = a.keys(opts) val = raw_input("Are you sure you want to delete %s tiles? (y/n) " % len(keys)) if val.lower() in ['y', 'yes']: for key in keys: a.deleteObject(key) else: parser.print_help() tilecache-2.11/TileCache/Caches/PaxHeader/Disk.py000644 777777 777777 00000000210 11456032102 023415 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311422 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Caches/Disk.py000644 473017214730172100000010247 11456032102 021615 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Cache import Cache import sys, os, time, warnings class Disk (Cache): def __init__ (self, base = None, umask = '002', **kwargs): Cache.__init__(self, **kwargs) self.basedir = base self.umask = int(umask, 0) if sys.platform.startswith("java"): from java.io import File self.file_module = File self.platform = "jython" else: self.platform = "cpython" if not self.access(base, 'read'): self.makedirs(base) def makedirs(self, path, hide_dir_exists=True): if hasattr(os, "umask"): old_umask = os.umask(self.umask) try: os.makedirs(path) except OSError, E: # os.makedirs can suffer a race condition because it doesn't check # that the directory doesn't exist at each step, nor does it # catch errors. This lets 'directory exists' errors pass through, # since they mean that as far as we're concerned, os.makedirs # has 'worked' if E.errno != 17 or not hide_dir_exists: raise E if hasattr(os, "umask"): os.umask(old_umask) def access(self, path, type='read'): if self.platform == "jython": if type == "read": return self.file_module(path).canRead() else: return self.file_module(path).canWrite() else: if type =="read": return os.access(path, os.R_OK) else: return os.access(path, os.W_OK) def getKey (self, tile): components = ( self.basedir, tile.layer.name, "%02d" % tile.z, "%03d" % int(tile.x / 1000000), "%03d" % (int(tile.x / 1000) % 1000), "%03d" % (int(tile.x) % 1000), "%03d" % int(tile.y / 1000000), "%03d" % (int(tile.y / 1000) % 1000), "%03d.%s" % (int(tile.y) % 1000, tile.layer.extension) ) filename = os.path.join( *components ) return filename def get (self, tile): filename = self.getKey(tile) if self.access(filename, 'read'): if self.sendfile: return filename else: tile.data = file(filename, "rb").read() return tile.data else: return None def set (self, tile, data): if self.readonly: return data filename = self.getKey(tile) dirname = os.path.dirname(filename) if not self.access(dirname, 'write'): self.makedirs(dirname) tmpfile = filename + ".%d.tmp" % os.getpid() if hasattr(os, "umask"): old_umask = os.umask(self.umask) output = file(tmpfile, "wb") output.write(data) output.close() if hasattr(os, "umask"): os.umask( old_umask ); try: os.rename(tmpfile, filename) except OSError: os.unlink(filename) os.rename(tmpfile, filename) tile.data = data return data def delete (self, tile): filename = self.getKey(tile) if self.access(filename, 'read'): os.unlink(filename) def attemptLock (self, tile): name = self.getLockName(tile) try: self.makedirs(name, hide_dir_exists=False) return True except OSError: pass try: st = os.stat(name) if st.st_ctime + self.stale < time.time(): warnings.warn("removing stale lock %s" % name) # remove stale lock self.unlock(tile) self.makedirs(name) return True except OSError: pass return False def unlock (self, tile): name = self.getLockName(tile) try: os.rmdir(name) except OSError, E: print >>sys.stderr, "unlock %s failed: %s" % (name, str(E)) tilecache-2.11/TileCache/Caches/PaxHeader/GoogleDisk.py000644 777777 777777 00000000210 11456032272 024562 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311429 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Caches/GoogleDisk.py000644 473017214730172100000003017 11456032272 022757 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2008-2010 TileCache Contributors """ Provides a subclass of Disk Cache which saves in a simple z/x/y.extension, with Y=0 being the top of the map, and heading down. This tile layout makes for generation of tiles that is friendly to Google/OSM, and the opposite of TMS. This is useful for pre-generating tiles for Google Maps which are going to be used offline. This allows one to use TileCache in a gdal2tiles-like setup, using the cache to write out a directory which can be used in other places. Note that ext3 (a common Linux filesystem) will not support more than 32000 files in a directory, so if you plan to store a whole world at z15 or greater, you should not use this cache class. (The Disk.py file is designed for this use case.) >>> from TileCache.Layer import Layer, Tile >>> l = Layer("test") >>> t = Tile(l, 14, 18, 12) >>> c = GoogleDisk("/tmp/tilecache") >>> c.getKey(t) '/tmp/tilecache/test/12/14/4077.png' """ from TileCache.Cache import Cache from TileCache.Caches.Disk import Disk import os class GoogleDisk(Disk): def getKey (self, tile): grid = tile.layer.grid(tile.z) components = ( self.basedir, tile.layer.name, "%s" % int(tile.z), "%s" % int(tile.x), "%s.%s" % (int(grid[1] - 1 - tile.y), tile.layer.extension) ) filename = os.path.join( *components ) return filename if __name__ == "__main__": import doctest doctest.testmod() tilecache-2.11/TileCache/Caches/PaxHeader/MBTiles.py000644 777777 777777 00000000210 11454752000 024026 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311423 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Caches/MBTiles.py000644 473017214730172100000001755 11454752000 022232 0ustar00christoschristos000000 000000 # A minimal implementation of an MBTiles-formatted SQLite database # cache access mechanism. (No writing, only reading.) # See: # http://mapbox.com/tools/mbtiles # for more information on the mbtiles format; it is essentially a single # table in a sqlite database with 4 columns: # * tile_column # * tile_row # * zoom_level # * tile_data from TileCache.Cache import Cache import os import sqlite3 class MBTiles (Cache): def __init__ (self, base = None, ext = None, umask = '002', **kwargs): Cache.__init__(self, **kwargs) self.basedir = base self.ext = ext def get (self, tile): db = sqlite3.connect("%s.%s" % (os.path.join(self.basedir, tile.layer.name), self.ext)) c = db.cursor() c.execute("select tile_data from tiles where tile_column=? and tile_row=? and zoom_level=?", (tile.x, tile.y, tile.z)) res = c.fetchone() if res: tile.data = str(res[0]) return tile.data return None tilecache-2.11/TileCache/Caches/PaxHeader/Memcached.py000644 777777 777777 00000000210 11456032102 024371 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311428 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Caches/Memcached.py000644 473017214730172100000002161 11456032102 022565 0ustar00christoschristos000000 000000 # BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors from TileCache.Cache import Cache import time class Memcached(Cache): def __init__ (self, servers = ['127.0.0.1:11211'], **kwargs): Cache.__init__(self, **kwargs) import memcache if type(servers) is str: servers = map(str.strip, servers.split(",")) self.cache = memcache.Client(servers, debug=0) def getKey(self, tile): return "/".join(map(str, [tile.layer.name, tile.x, tile.y, tile.z])) def get(self, tile): key = self.getKey(tile) tile.data = self.cache.get(key) return tile.data def set(self, tile, data): if self.readonly: return data key = self.getKey(tile) self.cache.set(key, data) return data def delete(self, tile): key = self.getKey(tile) self.cache.delete(key) def attemptLock (self, tile): return self.cache.add( self.getLockName(tile), "0", time.time() + self.timeout) def unlock (self, tile): self.cache.delete( self.getLockName(tile) ) tilecache-2.11/TileCache/Caches/PaxHeader/S3.py000644 777777 777777 00000000210 10736373331 023024 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311427 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Caches/S3.py000644 473017214730172100000051653 10736373331 021232 0ustar00christoschristos000000 000000 # This code is downloaded as part of the "Amazon S3 Library for REST in Python" # http://developer.amazonwebservices.com/connect/entry.jspa?externalID=134&categoryID=47 # It is taken verbatim from that download. # This software code is made available "AS IS" without warranties of any # kind. You may copy, display, modify and redistribute the software # code either by itself or as incorporated into your code; provided that # you do not remove any proprietary notices. Your use of this software # code is at your own risk and you waive any claim against Amazon # Digital Services, Inc. or its affiliates with respect to your use of # this software code. (c) 2006-2008 Amazon Digital Services, Inc. or its # affiliates. import base64 import hmac import httplib import re import sha import sys import time import urllib import urlparse import xml.sax DEFAULT_HOST = 's3.amazonaws.com' PORTS_BY_SECURITY = { True: 443, False: 80 } METADATA_PREFIX = 'x-amz-meta-' AMAZON_HEADER_PREFIX = 'x-amz-' # generates the aws canonical string for the given parameters def canonical_string(method, bucket="", key="", query_args={}, headers={}, expires=None): interesting_headers = {} for header_key in headers: lk = header_key.lower() if lk in ['content-md5', 'content-type', 'date'] or lk.startswith(AMAZON_HEADER_PREFIX): interesting_headers[lk] = headers[header_key].strip() # these keys get empty strings if they don't exist if not interesting_headers.has_key('content-type'): interesting_headers['content-type'] = '' if not interesting_headers.has_key('content-md5'): interesting_headers['content-md5'] = '' # just in case someone used this. it's not necessary in this lib. if interesting_headers.has_key('x-amz-date'): interesting_headers['date'] = '' # if you're using expires for query string auth, then it trumps date # (and x-amz-date) if expires: interesting_headers['date'] = str(expires) sorted_header_keys = interesting_headers.keys() sorted_header_keys.sort() buf = "%s\n" % method for header_key in sorted_header_keys: if header_key.startswith(AMAZON_HEADER_PREFIX): buf += "%s:%s\n" % (header_key, interesting_headers[header_key]) else: buf += "%s\n" % interesting_headers[header_key] # append the bucket if it exists if bucket != "": buf += "/%s" % bucket # add the key. even if it doesn't exist, add the slash buf += "/%s" % urllib.quote_plus(key) # handle special query string arguments if query_args.has_key("acl"): buf += "?acl" elif query_args.has_key("torrent"): buf += "?torrent" elif query_args.has_key("logging"): buf += "?logging" elif query_args.has_key("location"): buf += "?location" return buf # computes the base64'ed hmac-sha hash of the canonical string and the secret # access key, optionally urlencoding the result def encode(aws_secret_access_key, str, urlencode=False): b64_hmac = base64.encodestring(hmac.new(aws_secret_access_key, str, sha).digest()).strip() if urlencode: return urllib.quote_plus(b64_hmac) else: return b64_hmac def merge_meta(headers, metadata): final_headers = headers.copy() for k in metadata.keys(): final_headers[METADATA_PREFIX + k] = metadata[k] return final_headers # builds the query arg string def query_args_hash_to_string(query_args): query_string = "" pairs = [] for k, v in query_args.items(): piece = k if v != None: piece += "=%s" % urllib.quote_plus(str(v)) pairs.append(piece) return '&'.join(pairs) class CallingFormat: PATH = 1 SUBDOMAIN = 2 VANITY = 3 def build_url_base(protocol, server, port, bucket, calling_format): url_base = '%s://' % protocol if bucket == '': url_base += server elif calling_format == CallingFormat.SUBDOMAIN: url_base += "%s.%s" % (bucket, server) elif calling_format == CallingFormat.VANITY: url_base += bucket else: url_base += server url_base += ":%s" % port if (bucket != '') and (calling_format == CallingFormat.PATH): url_base += "/%s" % bucket return url_base build_url_base = staticmethod(build_url_base) class Location: DEFAULT = None EU = 'EU' class AWSAuthConnection: def __init__(self, aws_access_key_id, aws_secret_access_key, is_secure=True, server=DEFAULT_HOST, port=None, calling_format=CallingFormat.SUBDOMAIN): if not port: port = PORTS_BY_SECURITY[is_secure] self.aws_access_key_id = aws_access_key_id self.aws_secret_access_key = aws_secret_access_key self.is_secure = is_secure self.server = server self.port = port self.calling_format = calling_format def create_bucket(self, bucket, headers={}): return Response(self._make_request('PUT', bucket, '', {}, headers)) def create_located_bucket(self, bucket, location=Location.DEFAULT, headers={}): if location == Location.DEFAULT: body = "" else: body = "" + \ location + \ "" return Response(self._make_request('PUT', bucket, '', {}, headers, body)) def check_bucket_exists(self, bucket): return self._make_request('HEAD', bucket, '', {}, {}) def list_bucket(self, bucket, options={}, headers={}): return ListBucketResponse(self._make_request('GET', bucket, '', options, headers)) def delete_bucket(self, bucket, headers={}): return Response(self._make_request('DELETE', bucket, '', {}, headers)) def put(self, bucket, key, object, headers={}): if not isinstance(object, S3Object): object = S3Object(object) return Response( self._make_request( 'PUT', bucket, key, {}, headers, object.data, object.metadata)) def get(self, bucket, key, headers={}): return GetResponse( self._make_request('GET', bucket, key, {}, headers)) def delete(self, bucket, key, headers={}): return Response( self._make_request('DELETE', bucket, key, {}, headers)) def get_bucket_logging(self, bucket, headers={}): return GetResponse(self._make_request('GET', bucket, '', { 'logging': None }, headers)) def put_bucket_logging(self, bucket, logging_xml_doc, headers={}): return Response(self._make_request('PUT', bucket, '', { 'logging': None }, headers, logging_xml_doc)) def get_bucket_acl(self, bucket, headers={}): return self.get_acl(bucket, '', headers) def get_acl(self, bucket, key, headers={}): return GetResponse( self._make_request('GET', bucket, key, { 'acl': None }, headers)) def put_bucket_acl(self, bucket, acl_xml_document, headers={}): return self.put_acl(bucket, '', acl_xml_document, headers) def put_acl(self, bucket, key, acl_xml_document, headers={}): return Response( self._make_request( 'PUT', bucket, key, { 'acl': None }, headers, acl_xml_document)) def list_all_my_buckets(self, headers={}): return ListAllMyBucketsResponse(self._make_request('GET', '', '', {}, headers)) def get_bucket_location(self, bucket): return LocationResponse(self._make_request('GET', bucket, '', {'location' : None})) # end public methods def _make_request(self, method, bucket='', key='', query_args={}, headers={}, data='', metadata={}): server = '' if bucket == '': server = self.server elif self.calling_format == CallingFormat.SUBDOMAIN: server = "%s.%s" % (bucket, self.server) elif self.calling_format == CallingFormat.VANITY: server = bucket else: server = self.server path = '' if (bucket != '') and (self.calling_format == CallingFormat.PATH): path += "/%s" % bucket # add the slash after the bucket regardless # the key will be appended if it is non-empty path += "/%s" % urllib.quote_plus(key) # build the path_argument string # add the ? in all cases since # signature and credentials follow path args if len(query_args): path += "?" + query_args_hash_to_string(query_args) is_secure = self.is_secure host = "%s:%d" % (server, self.port) while True: if (is_secure): connection = httplib.HTTPSConnection(host) else: connection = httplib.HTTPConnection(host) final_headers = merge_meta(headers, metadata); # add auth header self._add_aws_auth_header(final_headers, method, bucket, key, query_args) connection.request(method, path, data, final_headers) resp = connection.getresponse() if resp.status < 300 or resp.status >= 400: return resp # handle redirect location = resp.getheader('location') if not location: return resp # (close connection) resp.read() scheme, host, path, params, query, fragment \ = urlparse.urlparse(location) if scheme == "http": is_secure = True elif scheme == "https": is_secure = False else: raise invalidURL("Not http/https: " + location) if query: path += "?" + query # retry with redirect def _add_aws_auth_header(self, headers, method, bucket, key, query_args): if not headers.has_key('Date'): headers['Date'] = time.strftime("%a, %d %b %Y %X GMT", time.gmtime()) c_string = canonical_string(method, bucket, key, query_args, headers) headers['Authorization'] = \ "AWS %s:%s" % (self.aws_access_key_id, encode(self.aws_secret_access_key, c_string)) class QueryStringAuthGenerator: # by default, expire in 1 minute DEFAULT_EXPIRES_IN = 60 def __init__(self, aws_access_key_id, aws_secret_access_key, is_secure=True, server=DEFAULT_HOST, port=None, calling_format=CallingFormat.SUBDOMAIN): if not port: port = PORTS_BY_SECURITY[is_secure] self.aws_access_key_id = aws_access_key_id self.aws_secret_access_key = aws_secret_access_key if (is_secure): self.protocol = 'https' else: self.protocol = 'http' self.is_secure = is_secure self.server = server self.port = port self.calling_format = calling_format self.__expires_in = QueryStringAuthGenerator.DEFAULT_EXPIRES_IN self.__expires = None # for backwards compatibility with older versions self.server_name = "%s:%s" % (self.server, self.port) def set_expires_in(self, expires_in): self.__expires_in = expires_in self.__expires = None def set_expires(self, expires): self.__expires = expires self.__expires_in = None def create_bucket(self, bucket, headers={}): return self.generate_url('PUT', bucket, '', {}, headers) def list_bucket(self, bucket, options={}, headers={}): return self.generate_url('GET', bucket, '', options, headers) def delete_bucket(self, bucket, headers={}): return self.generate_url('DELETE', bucket, '', {}, headers) def put(self, bucket, key, object, headers={}): if not isinstance(object, S3Object): object = S3Object(object) return self.generate_url( 'PUT', bucket, key, {}, merge_meta(headers, object.metadata)) def get(self, bucket, key, headers={}): return self.generate_url('GET', bucket, key, {}, headers) def delete(self, bucket, key, headers={}): return self.generate_url('DELETE', bucket, key, {}, headers) def get_bucket_logging(self, bucket, headers={}): return self.generate_url('GET', bucket, '', { 'logging': None }, headers) def put_bucket_logging(self, bucket, logging_xml_doc, headers={}): return self.generate_url('PUT', bucket, '', { 'logging': None }, headers) def get_bucket_acl(self, bucket, headers={}): return self.get_acl(bucket, '', headers) def get_acl(self, bucket, key='', headers={}): return self.generate_url('GET', bucket, key, { 'acl': None }, headers) def put_bucket_acl(self, bucket, acl_xml_document, headers={}): return self.put_acl(bucket, '', acl_xml_document, headers) # don't really care what the doc is here. def put_acl(self, bucket, key, acl_xml_document, headers={}): return self.generate_url('PUT', bucket, key, { 'acl': None }, headers) def list_all_my_buckets(self, headers={}): return self.generate_url('GET', '', '', {}, headers) def make_bare_url(self, bucket, key=''): full_url = self.generate_url(self, bucket, key) return full_url[:full_url.index('?')] def generate_url(self, method, bucket='', key='', query_args={}, headers={}): expires = 0 if self.__expires_in != None: expires = int(time.time() + self.__expires_in) elif self.__expires != None: expires = int(self.__expires) else: raise "Invalid expires state" canonical_str = canonical_string(method, bucket, key, query_args, headers, expires) encoded_canonical = encode(self.aws_secret_access_key, canonical_str) url = CallingFormat.build_url_base(self.protocol, self.server, self.port, bucket, self.calling_format) url += "/%s" % urllib.quote_plus(key) query_args['Signature'] = encoded_canonical query_args['Expires'] = expires query_args['AWSAccessKeyId'] = self.aws_access_key_id url += "?%s" % query_args_hash_to_string(query_args) return url class S3Object: def __init__(self, data, metadata={}): self.data = data self.metadata = metadata class Owner: def __init__(self, id='', display_name=''): self.id = id self.display_name = display_name class ListEntry: def __init__(self, key='', last_modified=None, etag='', size=0, storage_class='', owner=None): self.key = key self.last_modified = last_modified self.etag = etag self.size = size self.storage_class = storage_class self.owner = owner class CommonPrefixEntry: def __init(self, prefix=''): self.prefix = prefix class Bucket: def __init__(self, name='', creation_date=''): self.name = name self.creation_date = creation_date class Response: def __init__(self, http_response): self.http_response = http_response # you have to do this read, even if you don't expect a body. # otherwise, the next request fails. self.body = http_response.read() if http_response.status >= 300 and self.body: self.message = self.body else: self.message = "%03d %s" % (http_response.status, http_response.reason) class ListBucketResponse(Response): def __init__(self, http_response): Response.__init__(self, http_response) if http_response.status < 300: handler = ListBucketHandler() xml.sax.parseString(self.body, handler) self.entries = handler.entries self.common_prefixes = handler.common_prefixes self.name = handler.name self.marker = handler.marker self.prefix = handler.prefix self.is_truncated = handler.is_truncated self.delimiter = handler.delimiter self.max_keys = handler.max_keys self.next_marker = handler.next_marker else: self.entries = [] class ListAllMyBucketsResponse(Response): def __init__(self, http_response): Response.__init__(self, http_response) if http_response.status < 300: handler = ListAllMyBucketsHandler() xml.sax.parseString(self.body, handler) self.entries = handler.entries else: self.entries = [] class GetResponse(Response): def __init__(self, http_response): Response.__init__(self, http_response) response_headers = http_response.msg # older pythons don't have getheaders metadata = self.get_aws_metadata(response_headers) self.object = S3Object(self.body, metadata) def get_aws_metadata(self, headers): metadata = {} for hkey in headers.keys(): if hkey.lower().startswith(METADATA_PREFIX): metadata[hkey[len(METADATA_PREFIX):]] = headers[hkey] del headers[hkey] return metadata class LocationResponse(Response): def __init__(self, http_response): Response.__init__(self, http_response) if http_response.status < 300: handler = LocationHandler() xml.sax.parseString(self.body, handler) self.location = handler.location class ListBucketHandler(xml.sax.ContentHandler): def __init__(self): self.entries = [] self.curr_entry = None self.curr_text = '' self.common_prefixes = [] self.curr_common_prefix = None self.name = '' self.marker = '' self.prefix = '' self.is_truncated = False self.delimiter = '' self.max_keys = 0 self.next_marker = '' self.is_echoed_prefix_set = False def startElement(self, name, attrs): if name == 'Contents': self.curr_entry = ListEntry() elif name == 'Owner': self.curr_entry.owner = Owner() elif name == 'CommonPrefixes': self.curr_common_prefix = CommonPrefixEntry() def endElement(self, name): if name == 'Contents': self.entries.append(self.curr_entry) elif name == 'CommonPrefixes': self.common_prefixes.append(self.curr_common_prefix) elif name == 'Key': self.curr_entry.key = self.curr_text elif name == 'LastModified': self.curr_entry.last_modified = self.curr_text elif name == 'ETag': self.curr_entry.etag = self.curr_text elif name == 'Size': self.curr_entry.size = int(self.curr_text) elif name == 'ID': self.curr_entry.owner.id = self.curr_text elif name == 'DisplayName': self.curr_entry.owner.display_name = self.curr_text elif name == 'StorageClass': self.curr_entry.storage_class = self.curr_text elif name == 'Name': self.name = self.curr_text elif name == 'Prefix' and self.is_echoed_prefix_set: self.curr_common_prefix.prefix = self.curr_text elif name == 'Prefix': self.prefix = self.curr_text self.is_echoed_prefix_set = True elif name == 'Marker': self.marker = self.curr_text elif name == 'IsTruncated': self.is_truncated = self.curr_text == 'true' elif name == 'Delimiter': self.delimiter = self.curr_text elif name == 'MaxKeys': self.max_keys = int(self.curr_text) elif name == 'NextMarker': self.next_marker = self.curr_text self.curr_text = '' def characters(self, content): self.curr_text += content class ListAllMyBucketsHandler(xml.sax.ContentHandler): def __init__(self): self.entries = [] self.curr_entry = None self.curr_text = '' def startElement(self, name, attrs): if name == 'Bucket': self.curr_entry = Bucket() def endElement(self, name): if name == 'Name': self.curr_entry.name = self.curr_text elif name == 'CreationDate': self.curr_entry.creation_date = self.curr_text elif name == 'Bucket': self.entries.append(self.curr_entry) def characters(self, content): self.curr_text = content class LocationHandler(xml.sax.ContentHandler): def __init__(self): self.location = None self.state = 'init' def startElement(self, name, attrs): if self.state == 'init': if name == 'LocationConstraint': self.state = 'tag_location' self.location = '' else: self.state = 'bad' else: self.state = 'bad' def endElement(self, name): if self.state == 'tag_location' and name == 'LocationConstraint': self.state = 'done' else: self.state = 'bad' def characters(self, content): if self.state == 'tag_location': self.location += content tilecache-2.11/TileCache/Caches/PaxHeader/Test.py000644 777777 777777 00000000210 11010637741 023450 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311424 18 SCHILY.nlink=1 tilecache-2.11/TileCache/Caches/Test.py000644 473017214730172100000001062 11010637741 021643 0ustar00christoschristos000000 000000 from TileCache.Cache import Cache class Test(Cache): """ A Cache class which does not cache anything: useful for testing during development, or any other setup where TileCache should not cache data. In general, using this with metatiles is very slow, and not recommended. """ def get(self, tile): return None def set(self, tile, data): return data def getKey(self, tile): return "abc" def attemptLock(self, tile): return True def unlock(self, tile): pass tilecache-2.11/tests/PaxHeader/__init__.py000644 777777 777777 00000000210 11456027207 022427 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311437 18 SCHILY.nlink=1 tilecache-2.11/tests/__init__.py000644 473017214730172100000000040 11456027207 020615 0ustar00christoschristos000000 000000 from tests import run_doc_tests tilecache-2.11/tests/PaxHeader/HACKING.swarm.txt000644 777777 777777 00000000210 11456031665 023256 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311439 18 SCHILY.nlink=1 tilecache-2.11/tests/HACKING.swarm.txt000644 473017214730172100000002616 11456031665 021457 0ustar00christoschristos000000 000000 >>> from TileCache.Swarm import Message >>> from TileCache.Layer import Layer, Tile >>> from sha import sha >>> layers = { "Some Layer": Layer("Some Layer") } >>> key = sha("test").digest() >>> msg1 = Message("PING", key, 123456) >>> msg1.type == "PING" True >>> msg1.key == key True >>> msg1.seq_id == 123456 True >>> thunk = msg1.freeze() >>> thunk '\xa9J\x8f\xe5\xcc\xb1\x9b\xa6\x1cL\x08s\xd3\x91\xe9\x87\x98/\xbb\xd3\x00\x01\xe2@\x00\x00\x00\x00' >>> msg2 = Message.thaw(thunk, layers) >>> msg2.key == msg1.key True >>> msg2.seq_id == msg1.seq_id True >>> msg2.type == msg1.type True >>> tile = Tile( layers["Some Layer"], 8, 16, 32 ) >>> msg1 = Message( "GET", key, 54321, tile ) >>> msg1.tile == tile True >>> thunk = msg1.freeze() >>> thunk '\xa9J\x8f\xe5\xcc\xb1\x9b\xa6\x1cL\x08s\xd3\x91\xe9\x87\x98/\xbb\xd3\x00\x00\xd41\x00\x02\x00\x00\nSome Layer\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x08\x00\x00\x00\x10' >>> msg2 = Message.thaw( thunk, layers ) >>> msg2.type == "GET" True >>> msg1.tile.layer == msg2.tile.layer True >>> msg1.tile.x == msg2.tile.x True >>> msg1.tile.y == msg2.tile.y True >>> msg1.tile.z == msg2.tile.z True >>> tile.data = "here is some data" >>> msg1 = Message( "PUT", key, 101101, tile ) >>> msg2 = Message.thaw( msg1.freeze(), layers ) >>> msg2.type == "PUT" True >>> msg1.tile.data == msg2.tile.data True tilecache-2.11/tests/PaxHeader/HACKING.txt000644 777777 777777 00000000210 11007051140 022104 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311436 18 SCHILY.nlink=1 tilecache-2.11/tests/HACKING.txt000644 473017214730172100000006543 11007051140 020310 0ustar00christoschristos000000 000000 Handlers ======== There are two handlers for requests: a cgiHandler and a modPythonHandler. These two handlers allow the TileCache tool to be set up under CGI or mod_python. A handler accepts a Service as an argument. Services ======== The service request does dispatching to a Request handler, passing on the neccesary information to the parser and renderers of the request. The Service contains information about the cache and layers that are supported, and uses methods on the Request to generate data which is returned to the handlers. The Service makes a heuristic determination as to which Request handler is appropriate. Requests ======== Request objects define a single 'parse' function, which is used to accept PATH_INFO and QUERY_STRING data from the Service, and convert it to tiles. The Tile is then passed to the Layer, which knows how to return image data for a specific tile object. Cache ===== Cache objects are simple interfaces to cache storage mechanisms. They support three functions: get, set, and delete. These three functions are used to control the cache * get accepts a tile object, and is expected to return image data or 'None' * set accepts a tile object and data, and is expected to store this data. * delete accepts a tile object, and should cause future gets for that tile to return None (until set again) Layers ====== You can create a layer: >>> from TileCache.Layer import Layer >>> l = Layer("Test") >>> type(l) There are a number of defaults on the layer. These defaults conform to the unprojected global profile described by the WMS Tiling Client Recommendations: >>> l.name 'Test' >>> l.layers 'Test' >>> l.bbox (-180, -90, 180, 90) >>> l.resolutions[0] 0.703125 >>> l.srs 'EPSG:4326' >>> l.size (256, 256) By default, the Layer supports up to 20 resolutions, or 'zoom levels'. >>> len(l.resolutions) 20 However, you can create a layer which overrides these parameters: >>> l = Layer("Test 2", layers="foo,bar,baz", levels=10) >>> l.layers 'foo,bar,baz' >>> len(l.resolutions) 10 There are a number of subclasses of layer, which add additional properties for the layers. Subclasses of layer define a rendering mechanism through which tiles are actually created. To create a subclass of layer, you must define a 'render' function. Render is passed a 'self' object of the layer, and a Tile object, described later in this document. Refer to the existing layers for examples of how to create a subclass of layer. Layers have the ability to get a tile cell based on a bounding box: this calculation can be exact or it can round to the nearest cell. This is used internally when creating tiles, but can also be used directly by calling the layer with a bounding box. >>> cell = l.getCell((-157.5, -45.0, -135.0, -22.5)) >>> cell (1, 2, 3) This fails, because it's not an exact tile: >>> import TileCache >>> l.debug = False >>> try: ... cell = l.getCell((-180, -90.0, 4, 75)) ... except TileCache.TileCacheException: ... print "Failed" Failed Tiles ===== Tiles store information about where in the world you are requesting, based on the layer and x,y,z information. Tiles give access to X, Y, and Z value, as well as a bounds() and bbox() method. >>> from TileCache.Layer import Tile >>> t = Tile(l, x=1, y=2, z=3) >>> t.bbox() '-157.5,-45.0,-135.0,-22.5' >>> t.bounds() (-157.5, -45.0, -135.0, -22.5) tilecache-2.11/tests/PaxHeader/Service.txt000644 777777 777777 00000000210 11456031665 022462 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311440 18 SCHILY.nlink=1 tilecache-2.11/tests/Service.txt000644 473017214730172100000010273 11456031665 020661 0ustar00christoschristos000000 000000 The Service is the core component of the TileCache Server: various services (wsgi, cgi, mod_python) feed into it. You can see the properties:: >>> import TileCache.Service >>> filter(lambda x: not x.startswith("_"), dir(TileCache.Service)) ['cache', 'config', 'dispatchRequest', 'expireTile', 'files', 'generate_crossdomain_xml', 'layers', 'load', 'loadFromSection', 'metadata', 'renderTile', 'tilecache_options'] Or you can create one. In general, this is generated via the config file, but you can build one manually as well:: >>> from TileCache.Caches.Disk import Disk >>> from TileCache.Layer import Layer, Tile >>> l = Layer("basic", debug=False) >>> t = Tile(l, 0, 0, 0) >>> service = TileCache.Service(Disk("/tmp/tilecache"), {"layer": l}) >>> service # doctest: +ELLIPSIS The Service dispatchRequest method is what actually generates the tiles. It calls out to the layer's renderTile method. On the base Layer class, nothing is returned: all renderTile implementation is done by subclasses. As a result, rendering the tile fails on this layer:: >>> try: ... tile_data = service.dispatchRequest({}, path_info="/1.0.0/layer/0/0/0.png") ... except Exception, E: ... str(E) 'Zero length data returned from layer.' KML SuperOverlays can be generated as an alternative output to the TMS-style requests: simply change the image format to ".kml". >>> kml = service.dispatchRequest({}, path_info="/1.0.0/layer/0/0/0.kml") >>> kml[0] 'application/vnd.google-earth.kml+xml' >>> kml[1] '\n\n\n \n \n \n 256512\n \n \n 90.0-90.0\n 0.0-180.0\n \n \n \n 0\n \n http://example.com//1.0.0/basic/0/0/0\n \n \n 90.0-90.0\n 0.0-180.0\n \n \n \n tile\n \n \n 256-1\n \n \n 0.0-90.0\n -90.0-180.0\n \n \n \n http://example.com//1.0.0/basic/1/0/0.kml\n onRegion\n \n \n\n tile\n \n \n 256-1\n \n \n 0.0-90.0\n 0.0-90.0\n \n \n \n http://example.com//1.0.0/basic/1/1/0.kml\n onRegion\n \n \n\n tile\n \n \n 256-1\n \n \n 90.00.0\n 0.0-90.0\n \n \n \n http://example.com//1.0.0/basic/1/1/1.kml\n onRegion\n \n \n\n tile\n \n \n 256-1\n \n \n 90.00.0\n -90.0-180.0\n \n \n \n http://example.com//1.0.0/basic/1/0/1.kml\n onRegion\n \n \n \n' tilecache-2.11/tests/PaxHeader/tests.py000644 777777 777777 00000000210 10733210232 022017 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311438 18 SCHILY.nlink=1 tilecache-2.11/tests/tests.py000644 473017214730172100000002044 10733210232 020213 0ustar00christoschristos000000 000000 # stolen from shapely # http://trac.gispython.org/projects/PCL/browser/Shapely/trunk/tests/test_doctests.py # Copyright (c) 2007, Sean C. Gillies import doctest import unittest import glob import os optionflags = (doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS) def list_doctests(): return [filename for filename in glob.glob(os.path.join(os.path.dirname(__file__), '*.txt'))] def open_file(filename, mode='r'): """Helper function to open files from within the tests package.""" return open(os.path.join(os.path.dirname(__file__), filename), mode) def setUp(test): test.globs.update(dict( open_file = open_file, )) def run_doc_tests(): return unittest.TestSuite( [doctest.DocFileSuite(os.path.basename(filename), optionflags=optionflags, setUp=setUp) for filename in list_doctests()]) if __name__ == "__main__": runner = unittest.TextTestRunner() runner.run(run_doc_tests()) tilecache-2.11/docs/PaxHeader/_static000755 777777 777777 00000000210 11456036475 021467 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142717 24 SCHILY.dev=234881029 22 SCHILY.ino=3311451 18 SCHILY.nlink=3 tilecache-2.11/docs/_static/000755 473017214730172100000000000 11456036475 017735 5ustar00christoschristos000000 000000 tilecache-2.11/docs/PaxHeader/_templates000755 777777 777777 00000000210 11456036476 022177 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311459 18 SCHILY.nlink=2 tilecache-2.11/docs/_templates/000755 473017214730172100000000000 11456036476 020445 5ustar00christoschristos000000 000000 tilecache-2.11/docs/PaxHeader/Caches.txt000644 777777 777777 00000000210 11454752353 022040 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311449 18 SCHILY.nlink=1 tilecache-2.11/docs/Caches.txt000644 473017214730172100000006333 11454752353 020241 0ustar00christoschristos000000 000000 TileCache Caches ================ TileCache offers access to a number of different caches. These caches are used to store tiles. Disk ---- Example Configuration:: [cache] type=Disk base=/var/lib/tilecache umask=002 sendfile=yes expire=3600 Dependencies: None The sendfile=yes option instructs TileCache to send an X-SendFile header to the web server rather than the contents of the image. See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and http://tn123.ath.cx/mod_xsendfile/ GoogleDisk ---- Example Configuration:: [cache] type=GoogleDisk base=/var/lib/tilecache Dependencies: None Note: A simple subclass of the Disk cache, this switches the Y tile ordering on the disk, so Google Maps users can easily access tiles directly (e.g. bypassing tilecache.py, if all tiles have been pre-rendered) with a simple CustomTileUrl function. Memcached --------- Example configuration:: [cache] type=Memcached servers=127.0.0.1:11211 Dependencies: * memcache Python module must be installed on your system path. http://code.sixapart.com/svn/memcached/trunk/api/python/memcache.py Amazon S3 --------- Example configuration:: [cache] type=AWSS3 access_key=833833ABC88838 secret_access_key=8234abyi3kdjby8so8idu use_tms_paths=true The use_tms_paths will make the files more accessible to other clients, though at this time, ACLs are not set on the TMS structure to make them available publicly. The use_tms_paths can also be set to "flipped": if so, it will result in 'flipped' TMS, with typical paths like: /1.0.0/9/82/63.png But with 0 starting at the top of the map, instead of the bottom. This cache can use one of two libraries: Boto ____ Available from http://code.google.com/p/boto/, boto is a package that wraps the Amazon APIs in a Pythonic wrapper. This is installable on recent Debian-based distributions as python-boto. It can be installed via easy_install from setuptools as well: simply easy_install boto. The boto library is more complete, and is more likely to properly handle error conditions than the Amazon Example Library. Amazon Example Library ______________________ Amazon provides an example Python library. This library provides a simple wrapper around the Amazon web services. The code is available from http://developer.amazonwebservices.com/connect/entry.jspa?externalID=134&categoryID=47 You must download, unpack, and place the s3-example-libraries/python/S3.py file on your PYTHONPATH/sys.path. The source distribution of TileCache includes this file in the TileCache/Caches/S3.py file. (Packagers are encouraged to remove this file from distributions and instead depend on the boto library described above.) MBTiles ------- Simple read-only format for MBTiles sqlite databases. Name each database according to the name of the layer, and place it inside your cache directory, and provide an extension to your cache. [cache] type=MBTiles base=./tiles ext=db For layer basic, this will attempt to open a sqlite3 database at ./tiles/basic.db . See http://mapbox.com/tools/mbtiles for more details. All Caches ---------- All Caches support the expire option. The expire=n option instructs TileCache to send an Expires header, with the date set 'n' seconds into the future. tilecache-2.11/docs/PaxHeader/conf.py000644 777777 777777 00000000210 11456032272 021401 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311457 18 SCHILY.nlink=1 tilecache-2.11/docs/conf.py000644 473017214730172100000013460 11456032272 017601 0ustar00christoschristos000000 000000 # -*- coding: utf-8 -*- # # TileCache documentation build configuration file, created by # sphinx-quickstart on Sat Dec 27 06:07:30 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. #sys.path.append(os.path.abspath('.')) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.txt' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'TileCache' copyright = u'2008-2010, TileCache Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '2.11' # The full version, including alpha/beta/rc tags. release = '2.11' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'default.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, the reST sources are included in the HTML build as _sources/. #html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'TileCachedoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('index', 'TileCache.tex', ur'TileCache Documentation', ur'TileCache Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True tilecache-2.11/docs/PaxHeader/CONTRIBUTORS.txt000644 777777 777777 00000000210 11134764352 022604 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311454 18 SCHILY.nlink=1 tilecache-2.11/docs/CONTRIBUTORS.txt000644 473017214730172100000000527 11134764352 021004 0ustar00christoschristos000000 000000 Contributors ------------ The following people have contributed to TileCache development: | Tibor Arpas | Howard Butler | Schuyler Erle | Emily Gouge | Suki Hirata | Hani Howari | Andrew Hughes | Eric Lemoine | Steven Ottens | Matthew Perry | Bruce Rindahl | Tim Schaub | Christopher Schmidt | Ehud Shabtai | Dane Springmeyer | Bill Woodall tilecache-2.11/docs/PaxHeader/Example.py.txt000755 777777 777777 00000000210 10743307472 022675 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311446 18 SCHILY.nlink=1 tilecache-2.11/docs/Example.py.txt000755 473017214730172100000003403 10743307472 021071 0ustar00christoschristos000000 000000 #!/usr/bin/python # This example code demonstrates how you can construct # a custom service as an alternative to the config file. # This allows you to, for example, determine what layers to # load based on request parameters or something similar -- # the config file based loading technique is handy, but # probably doesn't solve all problems. from TileCache.Service import Service, modPythonHandler, cgiHandler from TileCache.Caches.Disk import Disk import TileCache.Layers.WMS as WMS import TileCache.Layers.MapServer as MS myService = Service ( Disk("/www/wms-c/cache"), { "basic" : MS.MapServer( "basic", "/www/wms-c/basic.map" ), "satellite" : MS.MapServer( "satellite", "/www/wms-c/basic.map", extension = "jpeg" ), "cascade" : WMS.WMS( "basic", "http://labs.metacarta.com/wms/vmap0?", extension = "jpeg" ), "DRG" : WMS.WMS( "DRG", "http://terraservice.net/ogcmap.ashx?", extension = "jpeg" ), "OSM" : WMS.WMS( "roads", "http://aesis.metacarta.com/cgi-bin/mapserv?FORMAT=png8&" + "map=/home/crschmidt/osm.map&TRANSPARENT=TRUE&", extension = "png" ), "Boston" : WMS.WMS( "border,water,openspace,roads,buildings,rapid_transit", "http://nyc.freemap.in/cgi-bin/mapserv?" + "map=/www/freemap.in/boston/map/gmaps.map&" ), "hfoot" : WMS.WMS( "hfoot", "http://beta.sedac.ciesin.columbia.edu/mapserver/wms/hfoot?", levels = 20, extension = "jpeg") } ) def handler (req): return modPythonHandler(req, myService) if __name__ == '__main__': cgiHandler(myService) tilecache-2.11/docs/PaxHeader/examples000755 777777 777777 00000000210 11456036476 021660 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311460 18 SCHILY.nlink=3 tilecache-2.11/docs/examples/000755 473017214730172100000000000 11456036476 020126 5ustar00christoschristos000000 000000 tilecache-2.11/docs/PaxHeader/EXAMPLES.txt000644 777777 777777 00000000210 11125407126 022056 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311456 18 SCHILY.nlink=1 tilecache-2.11/docs/EXAMPLES.txt000644 473017214730172100000002477 11125407126 020264 0ustar00christoschristos000000 000000 Layer Examples ============== Some Example Layer Configurations:: [mario-8-4] type=ImageLayer file=/home/images/mario-8-4.gif bbox=0,0,5889,224 filebounds=0,0,5889,224 maxresolution=16 [avalon] type=GDAL file=/home/images/avalon.jpg bbox=0,0,4000,1870 [avalon] type=ImageLayer filebounds=-180,-88.759,180,88.759 file=/home/images/avalon.jpg [basic] type=MapServerLayer layers=basic mapfile=/home/tilecache/basic.map debug=off description=VMap0-based vector layer, styled by Schuyler Erle. [DRG] type=WMSLayer layers=DRG url=http://terraservice.net/ogcmap.ashx? extension=jpeg debug=off [osm-map] type=MapnikLayer mapfile=/www/wms-c/osm.xml [massgis] type=ArcXML url=http://maps.massgis.state.ma.us/servlet/com.esri.esrimap.Esrimap?ServiceName=coq2005hsde_with_roads&ClientVersion=4.0 layers=14 bbox=218337.16112593404,889611.9317974453,248183.9359890727,914312.7109945256 [massgis-reprojected] type=ArcXML url=http://maps.massgis.state.ma.us/servlet/com.esri.esrimap.Esrimap?ServiceName=coq2005hsde_with_roads&ClientVersion=4.0 projection=@/path/to/target_projection # Either a string, or if prefixed by @, an absolute path to a projection in WKT. layers=14 bbox=218337.16112593404,889611.9317974453,248183.9359890727,914312.7109945256 tilecache-2.11/docs/PaxHeader/index.txt000644 777777 777777 00000000210 11125407126 021747 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311450 18 SCHILY.nlink=1 tilecache-2.11/docs/index.txt000644 473017214730172100000000666 11125407126 020153 0ustar00christoschristos000000 000000 .. TileCache documentation master file, created by sphinx-quickstart on Sat Dec 27 06:07:30 2008. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to TileCache's documentation! ===================================== Contents: .. toctree:: :maxdepth: 2 README Caches EXAMPLES SuperOverlays CONTRIBUTORS NEWS LICENSE * :ref:`search` tilecache-2.11/docs/PaxHeader/LICENSE.txt000644 777777 777777 00000000210 11456032102 021715 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311447 18 SCHILY.nlink=1 tilecache-2.11/docs/LICENSE.txt000644 473017214730172100000004145 11456032102 020115 0ustar00christoschristos000000 000000 License ------- Copyright (c) 2006-2010 TileCache Contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of TileCache nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The TileCache/Caches/S3.py file is distributed by Amazon as part of their S3 example library. The license for that file is: This software code is made available "AS IS" without warranties of any kind. You may copy, display, modify and redistribute the software code either by itself or as incorporated into your code; provided that you do not remove any proprietary notices. Your use of this software code is at your own risk and you waive any claim against Amazon Digital Services, Inc. or its affiliates with respect to your use of this software code. (c) 2006-2010 Amazon Digital Services, Inc. or its affiliates. tilecache-2.11/docs/PaxHeader/Makefile000644 777777 777777 00000000210 11125407126 021537 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311458 18 SCHILY.nlink=1 tilecache-2.11/docs/Makefile000644 473017214730172100000004477 11125407126 017747 0ustar00christoschristos000000 000000 # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html web pickle htmlhelp latex changes linkcheck help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf _build/* html: mkdir -p _build/html _build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." pickle: mkdir -p _build/pickle _build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle @echo @echo "Build finished; now you can process the pickle files." web: pickle json: mkdir -p _build/json _build/doctrees $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: mkdir -p _build/htmlhelp _build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p _build/changes _build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: mkdir -p _build/linkcheck _build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." tilecache-2.11/docs/PaxHeader/NEWS.txt000644 777777 777777 00000000210 11456027571 021425 xustar00christoschristos000000 000000 16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311453 18 SCHILY.nlink=1 tilecache-2.11/docs/NEWS.txt000644 473017214730172100000017442 11456027571 017631 0ustar00christoschristos000000 000000 Changelog ========= tilecache-2.11, Fri 15 Oct 2010 06:36:11 -0500 * Change copyright statements to be more accurate * Add minimal (read-only, non-optimized) support for the MBTiles sqlite format * Fix a bug introduced in disk locking prior to 2.0.3 * Add sendfile and Expires support to tilecache. * Other minor improvements to documentation and pre-seeding. tilecache-2.10, Sun, 18 Jan 2009 21:07:49 -0500 * APPLICATION CHANGE: tilecache_seed.py now uses optparse for options parsing, which may require changes in scripts which call tilecache_seed programatically. See the docs for the new style of command line. * Add support to S3 cache for TMS-style URLs (thx stvn) * Fixes for Caches/Disk.py under Jython * Support multiple TileCacheConfigs in the same mod_python server (thx fredj) * Add support for combining multiple tilecache layers into a single image when using WMS. (thx fredj) * allow custom mime_types in MapServer, WMS layers (thx fredj) * improve KML links to not link to invalid tiles (thx Graham Carlyle) * add png256 support to TileCache using Mapnik (thx Dane Springmeyer) * A number of other patches from the mailing list. tilecache-2.04, Mon, 23 Jun 2008 14:00:54 -0400 * Add support for JSON-output of layer information * Add 'data_extent' property to allow for informing remote clients of extent of data (seperate from grid extent) * Minor fixes to layer initialization with regard to spherical mercator, error logging, and others. tilecache-2.03, Mon, 19 May 2008 23:13:09 -0400 * Adding metadata about layers, etc. for allowing external clients to better understand the parameters used by a layer. * Improve programatic access to KML SuperOverlay output * Add SuperOverlay documentation designed to assist integrators. * Fix a bug in Memcache support with Metatiles. * Fix ordering of mod_python cache loading to load local paths first. tilecache-2.02, Sat, 10 May 2008 09:21:47 -0400 * Add GoogleDisk cache, to save tiles to disk in z/x/y.ext format * Add VETMS Service, provided by Steven Ottens * Add "Test" cache, which doesn't cache anything, for debugging * Fix Image Layer * Add GDAL Layer, for reading images via GDAL directly * Add support for reprojecting ArcXML layers * Add threading support to standalone http server * Support for Mapnik 0.5 * Support spitting out crossdomain.xml * Username/password support for WMS Layers * mod_python re-reads tilecache.cfg if it changed * Protect disk cache from os.makedirs race condition * Bump decimals in TMS,WMS-C units-per-pixel in caps output * Minor Changes in KML SuperOverlays tilecache-2.01, Thu, 27 Dec 2007 08:40:43 -0500 * Add Amazon S3 Cache support. * Add cache documentation * Improved error handling in server startup * Many minor cleanups of documentation, etc. tilecache-2.0, Sat, 22 Dec 2007 23:35:03 -0500 * Refactor TileCache to support drop in plugins for just about everything. * Upated TileCache release with KML SuperOverlay support * Add MGMaps service. * Add ArcXML layer. * Make code compatible with PyPI. * Cleaning up and moving around a bunch of distribution related stuff. tilecache-1.9, Wed, 5 Sep 2007 11:14:16 -0400 * Fix longstanding bug which made some non-geographic projections difficult to create tiles with * Add more informative error message output via TileCacheExceptions * Fix for "MetaLayer.getMetaSize is too conservative" * Report OSErrors more cleanly in tilecache_clean.py * Better support for Jython in DiskCache class. * Add support for extent_type=loose to allow out of bounds tile requests * Improved tilecache_seed to only grab one metaTile from each set -- this drastically reduces the number of requests to precache metatiled layers. Also fixed off-by-one error where seed was requesting too many tiles. * Add tms_type=google to specify that the TMS layer should go down instead of up. * Add setting of labelcache_map_edge_buffer to the MapServer layer if one does not exist on the map object. * Make mapnik layer respect layers= config if provided. * Remove getClosestLevel, no longer used or neccesary. * Better Lockfile removal, from Paul Spencer * Update WMS Capabilities, from Steven Ottens * Only use metaTiling if metatile=yes, true, or 1 * Minor change to WMS layer dispatching to handle GDAL WMS Driver compatability. tilecache-1.8, Fri, 4 May 2007 07:31:38 -0400 * Patch to ImageLayer from Volker Mische add: * Optional transparency to images which are not transparent. This allows the images to be transparent outside the maxExtent of the image. * Resize scaling method specification * Add support for tile watermarking, patch by Andrew Hughes. * Allow for definition of 'styles' parameter in WMSLayer tilecache cfg -- don't send blank parameters to the backend. tilecache-1.7, Fri, 13 Apr 2007 20:34:42 -0400 * Apache will sometimes kill TileCache, leaving around locks. If we can't lock for > cache.timeout seconds, throw an exception with the lockname, so that users can cleanup. Reported by Brock Anderson, who had stuck locks for several days. * If ImageLayer base image is transparent, make output Image transparent too. Report and patch by Brian Victor. * Add support for forcing client to reload on errors, by Suki Hirata. * Add support for not caching data which is not images for WMS, with original support written by Suki Hirata. * Minor fixups to TMS metadata, from Suki Hirata. tilecache-1.6, Thu Apr 5 19:09:58 EDT 2007 * Added Python distutils and Debian packaging materials. * Added CONTRIBUTORS and EXAMPLES documentation files. tilecache-1.5, Tue Mar 27 10:17:57 EDT 2007 * Client.py support reading Layer config from tilecache.cfg, from Hani Howari * Added ImageLayer. * Support for readonly caches * Add utility for cleaning a tilecache DiskCache to a certain size. * Minor distribution/documentation changes. tilecache-1.4, Sat Feb 3 21:56:02 EST 2007 * Added wsgi handler, from Ehud Shabtai. * Added standalone HTTP server, using wsgi handler, from Ehud Shabtai. * Added fastcgi implementation, using wsgi handler, from Ehud Shabtai. * Improved documentation, including patch from Eric Lemoine. * Support for 'maxResolution' layer option. * Improved support for running as CGI under IIS, from Suki Hirata. tilecache-1.3, Tue Dec 5 23:40:49 EST 2006 * Fix metaTile support: * Data was offset downwards by metaBuffer pixels. * metabuffer and metasize parsing was not working. * Add support for palatted transparency when using metaTiling. Patch by Matthew Perry * Fix for Windows support: os.rename() won't overwrite files. Patch by Tim Schaub * Added 'description' metadata as a layer property. * and <Abstract> support added to TMS metadata. * Documented layer configuration parameters. tilecache-1.2, Mon Dec 4 01:15:41 EST 2006 * Add support for WorldWind style requests, courtesy of Emily Gouge, from Refractions. * Add support for MapnikLayer for rendering. * Add support for rendering using metatiles: instead of rendering one tile at a time, render a 5x5 tileset, and then slice the tiles up. Good for vector layers, where labels crossing tile boundaries is useful. To turn on, use 'metaTile=yes' in layer definition. Requires Python-Imaging. * Improved TMS metadata * Fix TileCache to work on Python 2.2 * Fix TileCache to work on Windows. tilecache-1.1, Fri Nov 17 14:56:56 EST 2006 * Allow non-integer aspect ratios for bounding boxes * Add support for bounding box passed on command line to Client.py * Change cache to use local layer name for creating caches instead of remote layer list. After installing 1.1, if you have DiskCaches where the local layer name does not match the remote layer name, you should move your directory after installing tilecache-1.1. tilecache-1.0, Fri Nov 10 13:14:02 EST 2006 * Initial Release ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tilecache-2.11/docs/PaxHeader/README.txt������������������������������������������������������������000644 �777777 �777777 �00000000210 11456032102 021570� x����������������������������������������������������������������������������������������������������ustar�00christos������������������������christos������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311455 18 SCHILY.nlink=1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tilecache-2.11/docs/README.txt����������������������������������������������������������������������000644 �473017214730172100000042374 11456032102 017776� 0����������������������������������������������������������������������������������������������������ustar�00christos������������������������christos������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������================= Getting Started ================= ------------------------- Cache and serve map tiles ------------------------- :Author: labs@metacarta.com :Copyright: (c) 2006-2010 TileCache Contributors Distributed under the BSD license. :Version: 2.11 :Manual section: 8 :Manual group: GIS Utilities Description =========== TileCache is a BSD licensed tile caching mechanism. The goal is to make it easy to set up a WMS or TMS frontend to any backend data services you might be interested in, using a pluggable caching and rendering mechanism. TileCache was developed by MetaCarta Labs and released to the public under a BSD license. The TileCache was designed as a companion to OpenLayers, the BSD licensed web mapping interface. If you are using TileCache with OpenLayers, please read the section of this readme which describes how to do so. For additional help with setting up TileCache for use with OpenLayers, please feel free to stop by #openlayers, on irc.freenode.net, or to send email to tilecache@openlayers.org. Installing TileCache ==================== Generally, installing TileCache is as simple as downloading a source distribution and unpacking it. For installation systemwide, you can also use the Python Package Index (aka pypi or Cheeseshop) to install TileCache. Simply type easy_install TileCache. Once this is done, you will need to install the TileCache configuration file. A tool to do this is installed, called tilecache_install_config.py. A full installation likely looks like:: $ sudo easy_install TileCache ... Installed /usr/lib/python2.5/site-packages/TileCache-2.10-py2.5.egg $ sudo tilecache_install_config.py Successfully copied file /usr/lib/python2.5/site-packages/TileCache-2.10-py2.5.egg/TileCache/tilecache.cfg to /etc/tilecache.cfg. TileCache is also available as a Debian package from the TileCache homepage. This Debian package is designed to install on Debian etch releases or later. This Debian package should install on Ubuntu Feisty or Gutsy. Running Under CGI ================= * Extract the code to some web directory (e.g. in /var/www). * Edit tilecache.cfg to point the DiskCache to the location you wish to cache tiles, and the layers to point to the map file or WMS server you wish to cache. On Debian, this file is in /etc/tilecache.cfg by default. * Permit CGI execution in the TileCache directory. For example, if TileCache is to be run with Apache, the following must be added in your Apache configuration, where /var/www/tilecache is the directory resulting from the code extraction. On Debian, this is typically /usr/lib/cgi-bin. :: <Directory /var/www/tilecache> AddHandler cgi-script .cgi Options +ExecCGI </Directory> * Visit: http://example.com/yourdir/tilecache.cgi?LAYERS=basic&SERVICE=WMS &VERSION=1.1.1&REQUEST=GetMap&SRS=EPSG:4326&BBOX=-180,-90,0,90 &WIDTH=256&HEIGHT=256 * Or visit: http://example.com/yourdir/tilecache.cgi/1.0.0/basic/0/0/0.png * If you see a tile you have set up your configuration correctly. Congrats! Non-standard Python Location ---------------------------- If your Python is not at /usr/bin/python on your system, you will need to change the first line of tilecache.cgi to reference the location of your Python binary. A common example is: :: #!/usr/local/bin/python Under Apache, you might see an error message like: :: [Wed Mar 14 19:55:30 2007] [error] [client 127.0.0.1] (2)No such file or directory: exec of '/www/tilecache.cgi' failed to indicate this problem. You can typically locate where Python is installed on your system via the command which python. Windows users: If you are using Windows, you should change the first line of tilecache.cgi to read: :: #!C:/Python/python.exe -u C:/Python should match the location Python is installed under on your system. In Python 2.5, this location is C:/Python25 by default. Running Under mod_python ======================== * Extract the code to some web directory (e.g. /var/www). * Edit tilecache.cfg to point the DiskCache to the location you wish to cache tiles, and the layers to point to the map file or WMS server you wish to cache * Add the following to your Apache configuration, under a <Directory> heading: :: AddHandler python-program .py PythonHandler TileCache.Service PythonOption TileCacheConfig /path/to/tilecache.cfg * An example might look like: :: <Directory /var/www/tilecache/> AddHandler python-program .py PythonHandler TileCache.Service PythonOption TileCacheConfig /var/www/tilecache/tilecache.cfg </Directory> * In this example, /var/www/tilecache is the directory resulting from the code extraction. If you've installed this from a Debian package, the location of your .cfg file is probably /etc/tilecache.cfg. * Edit tilecache.cfg to point to the location of your 'Layers' directory, as demonstrated inside the default tilecache.cfg. * Visit one of the URLs described above, replacing tilecache.cgi with tilecache.py * If you see a tile you have set up your configuration correctly. Congrats! Running Standalone under WSGI ============================= TileCache as of version 1.4 comes with a standalone HTTP server which uses the WSGI handler. This implementation depends on *Python Paste*, which can be downloaded from: http://cheeseshop.python.org/pypi/Paste For versions of Python earlier than 2.5, you will also need to install wsgiref: http://cheeseshop.python.org/pypi/wsgiref Once you have all the prerequisites installed, simply run: :: python tilecache_http_server.py This will start a webserver listening on port 8080, after which you should be able to open: :: http://hostname:8080/1.0.0/basic/0/0/0.png to see your first tile. Running Under FastCGI ===================== TileCache as of version 1.4 comes with a fastcgi implementation. In order to use this implementation, you will need to install flup, available from: http://trac.saddi.com/flup This implementation also depends on Python Paste, which can be downloaded from: http://cheeseshop.python.org/pypi/Paste Once you have done this, you can configure your fastcgi server to use tilecache.fcgi. Configuring FastCGI is beyond the scope of this documentation. Running Under IIS ================= Installing TileCache for use with IIS requires some additional configuration. A nice document for setting up TileCache on IIS is available from Vish's weblog: http://viswaug.wordpress.com/2008/02/03/setting-up-tilecache-on-iis/ . Running Standalone with PasteScript and CherryPy ================================================ One component of the CherryPy web framework is a pure Python, fast, HTTP/1.1-compliant, WSGI thread-pooled webserver. To deploy Tilecache using this option you have to: * Install prerequisites: easy_install PasteScript easy_install CherryPy * Create a deployment config file specifying the http server and the application with options. The format of the configuration file is documented here: http://pythonpaste.org/deploy/#the-config-file Example configuration file follows. Copy the lines into tc.ini, tweak the tilecache_config variable, run paster serve tc.ini and enjoy at http://127.0.0.1:5000/tc :: [server:main] #tested with Paste#http and PasteScript#wsgiutils, PasteScript#twisted also possible after installing dependencies use = egg:PasteScript#cherrypy host = 127.0.0.1 port = 5000 [composite:main] use = egg:Paste#urlmap /tc = tilecache1 [app:tilecache1] use = egg:TileCache tilecache_config = tilecache.cfg Configuration ============= TileCache is configured by a config file, defaulting to tilecache.cfg. There are several parameters to control TileCache layers that are applicable to all layers: bbox The bounding box of the Layer. The resolutions array defaults to having resolutions which are equal to the bbox divided by 512 (two standard tiles). debug Whether to send debug output to the error.log. Defaults to "yes", can be set to "no" description Layer description, used in some metadata responses. Default is blank. extension File extension of the layer. Used to request images from WMS servers, as well as when writing cache files. layers A string used to describe the layers. Typically passed directly to the renderer. The WMSLayer sends this in the HTTP request, and the MapServerLayer chooses which layer to render based on this string. If no layer is provided, the layer name is used to fill this property. levels An integer, describing the number of 'zoom levels' or scales to support. Overridden by resolutions, if passed. mapfile The absolute file location of a mapfile. Required for MapServer and Mapnik layers. maxResolution The maximum resolution. If this is set, a resolutions array is automatically calculated up to a number of levels controlled by the 'levels' option. metaTile set to "yes" to turn on metaTiling. This will request larger tiles, and split them up using the Python Imaging library. Defaults to "no". metaBuffer an integer number of pixels to request around the outside of the rendered tile. This is good to combat edge effects in various map renderers. Defaults to 10. metaSize A comma seperated pair of integers, which is used to determine how many tiles should be rendered when using metaTiling. Default is 5,5. resolutions Comma seperate list of resolutions you want the TileCache instance to support. size Comma seperated set of integers, describing the width/height of the tiles. Defaults to 256,256 srs String describing the SRS value. Default is "EPSG:4326" type The type of layer. Options are: WMSLayer, MapnikLayer, MapServerLayer, ImageLayer url URL to use when requesting images from a remote WMS server. Required for WMSLayer. watermarkImage The watermarkImage parameter is assigned on a per-layer basis. This is a fully qualified path to an image you would like to apply to each tile. We recommend you use a watermark image the same size as your tiles. If using the default tile size, you should use a 256x256 image. NOTE: Python Imaging Library DOES NOT support interlaced images. watermarkOpacity The watermarkOpacity parameter is assigned on a per-layer basis. This configures the opacity of the watermark over the tile, it is a floating point number between 0 and 1. Usage is optional and will otherwise default. extent_type Setting this to 'loose' will allow TileCache to generate tiles outside the maximum bounding box. Useful for clients that don't know when to stop asking for tiles. tms_type Setting this to "google" will cause tiles to switch vertical order (that is, following the Google style x/y pattern). Using TileCache With OpenLayers =============================== To run OpenLayers with TileCache the URL passed to the OpenLayers.Layer.WMS constructor must point to the TileCache script, i.e. tilecache.cgi or tilecache.py. As an example see the index.html file included in the TileCache distribution. Note: index.html assumes TileCache is set up under CGI (see above). If you set up TileCache under mod_python you'd need to slighly modify index.html: the URL passed to the OpenLayers.Layer.WMS constructor must point to the mod_python script as opposed to the CGI script, so replace tilecache.cgi with tilecache.py. Similarly, you would need to edit this URL if you were to use TileCache with the standalone HTTP Server or FastCGI. The most important thing to do is to ensure that the OpenLayers Layer has the same resolutions and bounding box as your TileCache layer. You can define the resolutions in OpenLayers via the 'resolutions' option or the 'maxResolution' option on the layer. The maxExtent should be defined to match the bbox parameter of the TileCache layer. If you are using TileCache for overlays, you should set the 'reproject' option on the layer to 'false'. Using TileCache With MapServer ============================== MapServer has a map level metadata option, labelcache_map_edge_buffer, which is set automatically by TileCache to the metaBuffer plus five when metaTiling is on, if it is not set in the mapfile. If you are using MetaTiling, be aware that MapServer generates interlaced PNG files, which PIL will not read. See http://www.mapserver.org/faq.html#why-doesn-t-pil-python-imaging-library-open-my-pngs on how to resolve this. Using With Python-Mapscript =========================== Several users have reported cases where large mapfiles combined with python-mapscript has caused memory leaks, which eventually lead to segfaults. If you are having problems with Apache/TileCache segfaults when using python-mapscript, then you should switch to using a WMS Layer instead of a MapServer Layer. Seeding your TileCache ====================== The tilecache_seed.py utility will seed tiles in a cache automatically. You will need to have TileCache set up in one of the previously described configurations. Usage ----- tilecache_seed.py [options] <layer> [<zoom start> <zoom stop>] Options ------- --version show program's version number and exit -h, --help show this help message and exit -f, --force force recreation of tiles even if they are already in cache -b BBOX, --bbox=BBOX restrict to specified bounding box -p PADDING, --pading=PADDING extra margin tiles to seed around target area. Defaults to 0 (some edge tiles might be missing). A value of 1 ensures all tiles will be created, but some tiles may be wholly outside your bbox Arguments --------- layer same layer name that is in the tilecache.cfg zoom start Zoom level to start the process zoom end Zoom level to end the process Seeding by center point and radius ---------------------------------- If called without zoom level arguments, tilecache_seed.py will assume that it needs to read a list of points and radii from standard input, in the form: :: <lat>,<lon>,<radius> <lat>,<lon>,<radius> <lat>,<lon>,<radius> <lat>,<lon>,<radius> <ctrl + d> The format of this file is: lon the position(s) to seed longitude lat the position(s) to seed latitude radius the radius around the lon/lat to seed in degrees Examples -------- An example with zoom levels 5 through 12 and ~2 extra tiles around each zoom level would be like: :: $ tilecache_seed.py Zip_Codes 5 12 "-118.12500,31.952162238,-116.015625,34.3071438563" 2 The bbox can be dropped and defaults to world lonlat(-180,-90,180,90): :: $ tilecache_seed.py Zip_Codes 0 9 In center point/radius mode, the zoom level range is not specifiable from the command-line. An example usage might look like: :: $ tilecache_seed.py Zip_Codes -118.12500,31.952162238,0.05 -121.46327,32.345345645,0.08 <Ctrl+D> ... the seeding will then commence ... Cleaning your TileCache ======================= The tilecache_clean.py utility will remove the least recently accessed tiles from a cache, down to a specified size. Usage ----- tilecache_clean.py [options] <cache_location> Options ------- --version show program's version number and exit -h, --help show this help message and exit -s SIZE, --size=SIZE Maximum cache size, in megabytes. -e ENTRIES, --entries=ENTRIES Maximum cache entries. This limits the amount of memory that will be used to store information about tiles to remove. Notes ----- The --entries option to tilecache_clean.py is optional, and is used to regulate how much memory it uses to do its bookkeeping. The default value of 1 million will hopefully keep RAM utilization under about 100M on a 32-bit x86 Linux machine. If tilecache_clean.py doesn't appear to be keeping your disk cache down to an appropriate size, try upping this value. tilecache_clean.py is designed to be run from a cronjob like so: :: 00 05 * * * /usr/local/bin/tilecache_clean.py -s500 /var/www/tilecache Note that, on non-POSIX operating systems (particularly Windows), tilecache_clean.py measures file sizes, and not disk usage. Because most filesystems use entire file blocks for files smaller than a block, running du -s or similar on your disk cache after a cleaning may still return a total cache size larger than you expect. TroubleShooting =============== Occasionally, for some reason, when using meta tiles, your server may leave behind lock files. If this happens, there will be files in your cache directory with the extension '.lck'. If you are seeing tiles not render and taking multiple minutes before returning a 500 error, you may be suffering under a stuck lock. Removing all files with extension '.lck' from the cache directory will resolve this problem. SEE ALSO ======== memcached(8) http://tilecache.org/ http://openlayers.org/ http://wiki.osgeo.org/index.php/WMS_Tiling_Client_Recommendation http://wiki.osgeo.org/index.php/Tile_Map_Service_Specification ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tilecache-2.11/docs/PaxHeader/SuperOverlays.txt�����������������������������������������������������000644 �777777 �777777 �00000000210 11014437536 023470� x����������������������������������������������������������������������������������������������������ustar�00christos������������������������christos������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311448 18 SCHILY.nlink=1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tilecache-2.11/docs/SuperOverlays.txt���������������������������������������������������������������000644 �473017214730172100000006732 11014437536 021674� 0����������������������������������������������������������������������������������������������������ustar�00christos������������������������christos������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������Using KML SuperOverlays from TileCache ====================================== TileCache has support for KML superoverlays. For any TileCache layer for which the source data is EPSG:4326, the KML Service provides GroundOverlay data with LevelOfDetail NetworkLinks to deeper levels of data. How to Use ---------- In order to display the KML data, there are two options: * Create a link to a single tile which contains your tile of interest. The tiling scheme for your data determines what the tile identifiers are. Tiles match those used by TMS. * Create a service that assembles multiple KML tile links into a single KML document. This service should use NetworkLinks to point to other KML files to include. This service does not need to be comple: it simply needs to create small KML documents which include only a small number of networklinks, needed to link the user to an area. A simple document which does this -- for a given layername -- is included in doc/examples/overlay.kml. This sample document is designed to make available an entire worldwide set of tiles via TileCache: typically, one would create a more complex KML generator that would, for example, specify a LookAt tag as well, so that a user could open the data in Google Earth based on where they were looking. Also, this KML file will let them zoom out -- but it means that the server does need to generate (possibly many) KML documents to get the viewer to where they want to be, because the KML Client will need to trawl its way through the entire Pyramid down to where the user is viewing. However, the image data is not included with the KML document, so this should be a lightweight server side operation: it's just somewhat slow to do many round trips, in general, so specifying (as a top level) some reasonaable compromise between the whole world and the targeted viewing plane might make sense. Finding KML/Tile URLS --------------------- TileCache KML documents are based around the TMS URL scheme. This means that for a TMS tile: http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap/0/0/0.png the corresponding KML document is http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap/0/0/0.kml By default, TileCache uses a 'whole world' extent, split into two tiles: western hemisphere and eastern. This means that to get the whole world into Google Earth, you would need to include links to 0/0/0.kml and 0/0/1.kml. TileCache can calculate a tile z/x/y from a bounding box using the getCell function on a layer. To use this:: >>> import TileCache.Service >>> s = TileCache.Service.load("tilecache.cfg") >>> s.layers # doctest: +ELLIPSIS {'basic': <TileCache.Layers.WMS.WMS object at ...>} >>> s.layers['basic'] # doctest: +ELLIPSIS <TileCache.Layers.WMS.WMS object at ...> >>> basic = s.layers['basic'] >>> basic.getCell((-180,-90,0,90)) (0, 0, 0) >>> cell = basic.getCell((-10,-90,12,-80), exact=False) >>> cell (8, 0, 3) Once you've done this, you can then use Python to construct a KML doc for the tile:: >>> from TileCache.Layer import Tile >>> tile = Tile(basic, cell[0], cell[1], cell[2]) >>> from TileCache.Services.KML import KML >>> kml = KML(s) >>> doc = kml.generate_kml_doc(tile, base_path="http://example.com/tilecache.cgi", include_wrapper=False) >>> len(doc) 2546 >>> doc[550:600] '\n </GroundOverlay>\n <NetworkLink>\n <nam' .. test runner: python -c "import doctest; doctest.testfile('docs/SuperOverlays.txt', optionflags=doctest.ELLIPSIS)" ��������������������������������������tilecache-2.11/docs/examples/PaxHeader/overlay.kml��������������������������������������������������000644 �777777 �777777 �00000000210 11014426164 024103� x����������������������������������������������������������������������������������������������������ustar�00christos������������������������christos������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������16 gid=10322897 16 uid=10322897 20 ctime=1287142718 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311461 18 SCHILY.nlink=1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tilecache-2.11/docs/examples/overlay.kml������������������������������������������������������������000644 �473017214730172100000000615 11014426164 022301� 0����������������������������������������������������������������������������������������������������ustar�00christos������������������������christos������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <kml xmlns="http://earth.google.com/kml/2.1"> <Folder> <NetworkLink> <Link> <href>http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap/0/0/0.kml</href> </Link> </NetworkLink> <NetworkLink> <Link> <href>http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap/0/1/0.kml</href> </Link> </NetworkLink> </Folder> </kml> �������������������������������������������������������������������������������������������������������������������tilecache-2.11/docs/_static/PaxHeader/default.css���������������������������������������������������000644 �777777 �777777 �00000000210 11125407126 023663� x����������������������������������������������������������������������������������������������������ustar�00christos������������������������christos������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������16 gid=10322897 16 uid=10322897 20 ctime=1287142717 20 atime=1287142718 24 SCHILY.dev=234881029 22 SCHILY.ino=3311452 18 SCHILY.nlink=1 ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tilecache-2.11/docs/_static/default.css�������������������������������������������������������������000644 �473017214730172100000032356 11125407126 022070� 0����������������������������������������������������������������������������������������������������ustar�00christos������������������������christos������������������������000000 �000000 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������/** * Sphinx Doc Design */ body { font-family: sans-serif; font-size: 100%; color: #000; margin: 0; padding: 0; } /* :::: LAYOUT :::: */ div.documentwrapper { float: left; width: 100%; } div.bodywrapper { margin: 0 0 0 230px; } div.body { background-color: white; padding: 0 20px 30px 20px; border-left:1px dotted #EE3124; } div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: left; width: 230px; margin-left: -100%; font-size: 90%; } div.clearer { clear: both; } div.footer { width: 100%; padding: 9px 0 9px 0; text-align: center; font-size: 75%; } div.footer a { text-decoration: underline; } div.related { background-color: white; color: #000; width: 100%; line-height: 30px; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } div.related a { color: #EE3124; } /* ::: TOC :::: */ div.sphinxsidebar h3 { font-family: Arial, sans-serif; color: black; background-color: white; font-size: 1.4em; font-weight: normal; margin: 0; padding: 0; } div.sphinxsidebar h3 a { color: #EE3124; } div.sphinxsidebar h4 { font-family: Arial, sans-serif; color: black; font-size: 1.3em; font-weight: normal; margin: 5px 0 0 0; padding: 0; } div.sphinxsidebar p { color: black; } div.sphinxsidebar p.topless { margin: 5px 10px 10px 10px; } div.sphinxsidebar ul { margin: 10px; padding: 0; list-style: none; color: black; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar a { } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } /* :::: MODULE CLOUD :::: */ div.modulecloud { margin: -5px 10px 5px 10px; padding: 10px; line-height: 160%; border: 1px solid #cbe7e5; background-color: #f2fbfd; } div.modulecloud a { padding: 0 5px 0 5px; } /* :::: SEARCH :::: */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* :::: COMMON FORM STYLES :::: */ div.actions { padding: 5px 10px 5px 10px; border-top: 1px solid #cbe7e5; border-bottom: 1px solid #cbe7e5; background-color: #e0f6f4; } form dl { color: #333; } form dt { clear: both; float: left; min-width: 110px; margin-right: 10px; padding-top: 2px; } input#homepage { display: none; } div.error { margin: 5px 20px 0 0; padding: 5px; border: 1px solid #d00; font-weight: bold; } /* :::: INLINE COMMENTS :::: */ div.inlinecomments { position: absolute; right: 20px; } div.inlinecomments a.bubble { display: block; float: right; background-image: url(style/comment.png); background-repeat: no-repeat; width: 25px; height: 25px; text-align: center; padding-top: 3px; font-size: 0.9em; line-height: 14px; font-weight: bold; color: black; } div.inlinecomments a.bubble span { display: none; } div.inlinecomments a.emptybubble { background-image: url(style/nocomment.png); } div.inlinecomments a.bubble:hover { background-image: url(style/hovercomment.png); text-decoration: none; color: #3ca0a4; } div.inlinecomments div.comments { float: right; margin: 25px 5px 0 0; max-width: 50em; min-width: 30em; border: 1px solid #2eabb0; background-color: #f2fbfd; z-index: 150; } div#comments { border: 1px solid #2eabb0; margin-top: 20px; } div#comments div.nocomments { padding: 10px; font-weight: bold; } div.inlinecomments div.comments h3, div#comments h3 { margin: 0; padding: 0; background-color: #2eabb0; color: black; border: none; padding: 3px; } div.inlinecomments div.comments div.actions { padding: 4px; margin: 0; border-top: none; } div#comments div.comment { margin: 10px; border: 1px solid #2eabb0; } div.inlinecomments div.comment h4, div.commentwindow div.comment h4, div#comments div.comment h4 { margin: 10px 0 0 0; background-color: #2eabb0; color: black; border: none; padding: 1px 4px 1px 4px; } div#comments div.comment h4 { margin: 0; } div#comments div.comment h4 a { color: #d5f4f4; } div.inlinecomments div.comment div.text, div.commentwindow div.comment div.text, div#comments div.comment div.text { margin: -5px 0 -5px 0; padding: 0 10px 0 10px; } div.inlinecomments div.comment div.meta, div.commentwindow div.comment div.meta, div#comments div.comment div.meta { text-align: right; padding: 2px 10px 2px 0; font-size: 95%; color: #538893; border-top: 1px solid #cbe7e5; background-color: #e0f6f4; } div.commentwindow { position: absolute; width: 500px; border: 1px solid #cbe7e5; background-color: #f2fbfd; display: none; z-index: 130; } div.commentwindow h3 { margin: 0; background-color: #2eabb0; color: black; border: none; padding: 5px; font-size: 1.5em; cursor: pointer; } div.commentwindow div.actions { margin: 10px -10px 0 -10px; padding: 4px 10px 4px 10px; color: #538893; } div.commentwindow div.actions input { border: 1px solid #2eabb0; background-color: white; color: #135355; cursor: pointer; } div.commentwindow div.form { padding: 0 10px 0 10px; } div.commentwindow div.form input, div.commentwindow div.form textarea { border: 1px solid #3c9ea2; background-color: white; color: black; } div.commentwindow div.error { margin: 10px 5px 10px 5px; background-color: #fbe5dc; display: none; } div.commentwindow div.form textarea { width: 99%; } div.commentwindow div.preview { margin: 10px 0 10px 0; background-color: #70d0d4; padding: 0 1px 1px 25px; } div.commentwindow div.preview h4 { margin: 0 0 -5px -20px; padding: 4px 0 0 4px; color: black; font-size: 1.3em; } div.commentwindow div.preview div.comment { background-color: #f2fbfd; } div.commentwindow div.preview div.comment h4 { margin: 10px 0 0 0!important; padding: 1px 4px 1px 4px!important; font-size: 1.2em; } /* :::: SUGGEST CHANGES :::: */ div#suggest-changes-box input, div#suggest-changes-box textarea { border: 1px solid #ccc; background-color: white; color: black; } div#suggest-changes-box textarea { width: 99%; height: 400px; } /* :::: PREVIEW :::: */ div.preview { background-image: url(style/preview.png); padding: 0 20px 20px 20px; margin-bottom: 30px; } /* :::: INDEX PAGE :::: */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* :::: INDEX STYLES :::: */ table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } form.pfform { margin: 10px 0 20px 0; } /* :::: GLOBAL STYLES :::: */ .docwarning { background-color: #ffe4e4; padding: 10px; margin: 0 -20px 0 -20px; border-bottom: 1px solid #f66; } p.subhead { font-weight: bold; margin-top: 20px; } a { text-decoration: none; } a:hover { color: #EE3124; text-decoration: underline; } div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 { font-family: Arial, sans-serif; font-weight: normal; color: black; margin: 20px -20px 10px -20px; padding: 3px 0 3px 10px; } div.body h1 { margin-top: 0; font-size: 200%; } div.body h2 { font-size: 160%; } div.body h3 { font-size: 140%; } div.body h4 { font-size: 120%; } div.body h5 { font-size: 110%; } div.body h6 { font-size: 100%; } a.headerlink { color: #c60f0f; font-size: 0.8em; padding: 0 4px 0 4px; text-decoration: none; visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } a.headerlink:hover { background-color: #c60f0f; color: black; } div.body p, div.body dd, div.body li { text-align: justify; line-height: 130%; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } ul.fakelist { list-style: none; margin: 10px 0 10px 20px; padding: 0; } .field-list ul { padding-left: 1em; } .first { margin-top: 0 !important; } /* "Footnotes" heading */ p.rubric { margin-top: 30px; font-weight: bold; } /* Sidebars */ div.sidebar { margin: 0 0 0.5em 1em; border: 1px solid #ddb; padding: 7px 7px 0 7px; background-color: #ffe; width: 40%; float: right; } p.sidebar-title { font-weight: bold; } /* "Topics" */ div.topic { background-color: #eee; border: 1px solid #ccc; padding: 7px 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* Admonitions */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } div.admonition p.admonition-title + p { display: inline; } div.seealso { background-color: #ffc; border: 1px solid #ff6; } div.warning { background-color: #ffe4e4; border: 1px solid #f66; } div.note { background-color: #eee; border: 1px solid #ccc; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; display: inline; } p.admonition-title:after { content: ":"; } div.body p.centered { text-align: center; margin-top: 25px; } table.docutils { border: 0; } table.docutils td, table.docutils th { padding: 1px 8px 1px 0; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #aaa; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } dl { margin-bottom: 15px; clear: both; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } .refcount { color: #060; } dt:target, .highlight { background-color: #fbe54e; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } th { text-align: left; padding-right: 5px; } pre { padding: 5px; background-color: #efc; color: #333; border: 1px solid #ac9; border-left: none; border-right: none; overflow: auto; } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt { background-color: #ecf0f3; padding: 0 1px 0 1px; font-size: 0.95em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } .footnote:target { background-color: #ffa } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } form.comment { margin: 0; padding: 10px 30px 10px 30px; background-color: #eee; } form.comment h3 { background-color: #326591; color: black; margin: -10px -30px 10px -30px; padding: 5px; font-size: 1.4em; } form.comment input, form.comment textarea { border: 1px solid #ccc; padding: 2px; font-family: sans-serif; font-size: 100%; } form.comment input[type="text"] { width: 240px; } form.comment textarea { width: 100%; height: 200px; margin-bottom: 10px; } .system-message { background-color: #fda; padding: 5px; border: 3px solid red; } img.math { vertical-align: middle; } div.math p { text-align: center; } span.eqno { float: right; } img.logo { border: 0; } /* :::: PRINT :::: */ @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0; width : 100%; } div.sphinxsidebar, div.related, div.footer, div#comments div.new-comment-box, #top-link { display: none; } } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������