diff --git a/README.md b/README.md index 6249834..4651402 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,19 @@ var cities = [ ]; ``` +If you have a GeoNames account, or you have manually put latitude and longitude +into your `core_places` table, you can run the `map_places` admin command to +generate valid JavaScript: + + # Run this from the open-oni directory + docker-compose exec web manage map_places geonames-user OR + +You can even automate it further by grepping only the first line of JS: + + # Run this from the open-oni directory + docker-compose exec web manage map_places geonames-user OR | \ + grep -A999999999 "var cities = " >themes/oregon/static/js/cities_list.js + To hitch this plugin to your project wagon, you will need to add a few lines in some files. `onisite.plugins.map` needs to go in your `INSTALLED_APPS` list: diff --git a/management/__init__.py b/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/management/commands/__init__.py b/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/management/commands/map_places.py b/management/commands/map_places.py new file mode 100644 index 0000000..d2b4db6 --- /dev/null +++ b/management/commands/map_places.py @@ -0,0 +1,97 @@ +import json +import logging +import urllib + +from django.core.management.base import BaseCommand +from django.db import reset_queries + +from core import models +from core.management.commands import configure_logging + +configure_logging("openoni_map_places.config", "openoni_map_places.log") +_logger = logging.getLogger("map_places") +geonames_url="http://api.geonames.org/searchJSON" + +class Command(BaseCommand): + def add_arguments(self, parser): + parser.add_argument("username", action="store", + help="Username for the GeoNames API; set to 'demo' if you don't plan to pull any GeoNames data") + parser.add_argument('state', action='store', + help='State code (e.g., OR, NE, etc) for restricting the search results'), + + def handle(self, *args, **options): + _logger.debug("Finding places in Geonames") + + # Gather up all the places' latitude and longitude data + output_data = {} + for place in models.Place.objects.all(): + if not place.city: + _logger.error("A place with no city exists in your database (%s)! " + + "This is probably A Bad Thing (tm)." % place.name) + continue + + # First check for lat/lng in the database + lat, lng = place.latitude, place.longitude + + # If we didn't have lat/lng and the username was specified, check geonames + if lat is None and lng is None and options["username"] != "demo": + lat, lng = _get_lat_long_from_geonames(place, options) + + # If we retrieved data, make sure we save it + if lat is not None and lng is not None: + place.latitude = lat + place.longitude = lng + place.save() + reset_queries() + + # Record the lat/lng in our output data + if lat is not None and lng is not None: + output_data[place] = (lat,lng) + + _logger.info("finished looking up places in GeoNames") + + cities_json = [] + for place in output_data: + titles = models.Title.objects.filter(places__city__iexact=place.city).all() + if len(titles) == 0: + continue + + city_json = { + "name": place.city, + "latlong": [place.latitude, place.longitude], + "papers": {} + } + for title in titles: + city_json["papers"][title.display_name] = title.lccn + + cities_json.append(city_json) + + # Populate the JSON with all the titles + + self.stdout.write("***********************") + self.stdout.write(" Copy below:") + self.stdout.write("***********************") + self.stdout.write("var cities = " + json.dumps(cities_json, indent=2) + ";") + + +def _get_lat_long_from_geonames(place, options): + url_query = { + "name_equals":place.city, + "maxRows":1, + "username":options["username"], + "adminCode1":options["state"], + "featureClass":"P", + "country":"US", + } + url = "%s?%s" % (geonames_url, urllib.urlencode(url_query)) + _logger.info("Querying %s" % url) + + h = urllib.urlopen(url) + data = h.read() + geodata = json.loads(data) + + if geodata is not None and "geonames" in geodata and len(geodata["geonames"]) == 1: + return geodata["geonames"][0]["lat"], geodata["geonames"][0]["lng"] + else: + _logger.error("Error trying to look up %s; raw data was %s", url, data) + return None, None