diff --git a/README b/README
new file mode 100644
index 0000000..b553354
--- /dev/null
+++ b/README
@@ -0,0 +1,15 @@
+This is a visualization of the Linked Open Data cloud using the ProtoVis
+JavaScript library. The underlying data is generated by talking to the
+Comprehensive Knowledge Archive Network (CKAN) API.
+
+To generate the lod.js data file you should be able to run ckan.py:
+
+ ./ckan.py > lod.js
+
+It can take a few minutes to run, so go make yourself a cup of tea while
+it's running. After that you should be able to load index.html in
+your browser.
+
+Ed Summers
+ehs@pobox.com
+
diff --git a/ckan.py b/ckan.py
new file mode 100755
index 0000000..9b70d9e
--- /dev/null
+++ b/ckan.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+"""
+This is a little script that will pull down all the package information
+for packages in the lodcloud group on CKAN, and write out JSON dataset
+for ProtoVis.
+"""
+
+import sys
+import json
+import urllib
+import logging
+
+
+LOG_FILE = "ckan.log"
+LOG_LEVEL = logging.INFO
+
+
+def main(argv):
+ logging.basicConfig(filename=LOG_FILE, level=LOG_LEVEL)
+ packages = lod_packages()
+ protovis = protovis_json(packages)
+ print "var lod = " + json.dumps(protovis, indent=2)
+
+
+def lod_packages():
+ log = logging.getLogger()
+ packages = []
+ count = 0
+ for package in ckan('group/lodcloud')['packages']:
+ package_info = ckan('package/%s' % package)
+ package_info['internal_id'] = count
+ packages.append(package_info)
+ log.info("got info for %s" % package_info['name'])
+ count += 1
+ return packages
+
+
+def protovis_json(packages):
+ protovis = {'nodes': get_nodes(packages),
+ 'links': get_links(packages)}
+ return protovis
+
+
+def get_nodes(packages):
+ nodes = []
+ for package in packages:
+ if package['ratings_average'] == None:
+ rating = 0
+ else:
+ rating = int(round(float(package['ratings_average'])))
+ nodes.append({
+ 'rating': rating,
+ 'nodeName': package['title']})
+ return nodes
+
+
+def get_links(packages):
+ log = logging.getLogger()
+
+ # first get a dictionary lookup for all the packages by name
+ package_map = {}
+ for package in packages:
+ package_map[package['name']] = package
+
+ # now generate links based on the numeric id of the package
+ links = []
+ for from_package in packages:
+ for key in from_package['extras']:
+ if key.startswith('links:'):
+ to_package_name = key.split(':')[1]
+ if not package_map.has_key(to_package_name):
+ log.error("%s has link to %s which doesn't exist" % \
+ (from_package['name'], to_package_name))
+ continue
+ links.append({
+ 'source': from_package['internal_id'],
+ 'target': package_map[to_package_name]['internal_id']})
+ return links
+
+
+def ckan(path):
+ j = urllib.urlopen('http://ckan.net/api/rest/' + path).read()
+ return json.loads(j)
+
+
+def configure_logging():
+ logging.basicConfig()
+ logger = logging.getLogger()
+ logger.setLevel(logging.INFO)
+ handler = logging.FileHandler(log_file)
+ formatter = logging.Formatter("""[%(asctime)s %(levelname)s %(name)s] %(message)s""")
+ handler.setFormatter(formatter)
+ logger.addHandler(handler)
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..3f132fe
--- /dev/null
+++ b/index.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+ This is a touchgraph version of the
+ Linked Open Data Cloud.
+ It is based on the same underlying data using the
+ Comprehensive Knowledge Archive Network (CKAN)
+ API
+ to pull all the packages in the the
+ lodcloud group.
+ If your screen goes crazy cross your fingers and try to
+ reload the page a couple times. The colors reflect the
+ CKAN rating: (0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5)
+ More about how this graph was
+ generated can be found on
+ Github.
+ Comments/questions welcome at
+ ehs@pobox.com.
+
+
+
+
+
+
+