Skip to content

Commit 1ec963a

Browse files
committed
Initial commit
0 parents  commit 1ec963a

17 files changed

+506
-0
lines changed

data/placeholder.txt

Whitespace-only changes.

geo/__init__.py

Whitespace-only changes.

geo/api.py

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from django.contrib.gis.geos import Point
2+
from tastypie.resources import ModelResource
3+
from tastypie.exceptions import InvalidFilterError
4+
from tastypie.constants import ALL, ALL_WITH_RELATIONS
5+
from tastypie.contrib.gis.resources import ModelResource as GeometryModelResource
6+
from tastypie import fields
7+
from geo.models import *
8+
import json
9+
10+
class MunicipalityResource(ModelResource):
11+
def _convert_to_geojson(self, bundle):
12+
muni = bundle.obj
13+
data = {'type': 'Feature'}
14+
data['properties'] = bundle.data
15+
data['id'] = bundle.obj.pk
16+
borders = muni.municipalityboundary.borders
17+
data['geometry'] = json.loads(borders.geojson)
18+
bundle.data = data
19+
return bundle
20+
def alter_detail_data_to_serialize(self, request, bundle):
21+
if request.GET.get('format') == 'geojson':
22+
return self._convert_to_geojson(bundle)
23+
return bundle
24+
def alter_list_data_to_serialize(self, request, bundles):
25+
if request.GET.get('format') != 'geojson':
26+
return bundles
27+
data = {'type': 'FeatureCollection'}
28+
data['meta'] = bundles['meta']
29+
data['features'] = [self._convert_to_geojson(bundle) for bundle in bundles['objects']]
30+
return data
31+
def apply_filters(self, request, filters):
32+
obj_list = super(MunicipalityResource, self).apply_filters(request, filters)
33+
if request.GET.get('format') == 'geojson':
34+
obj_list = obj_list.select_related('municipalityboundary')
35+
return obj_list
36+
def dehydrate(self, bundle):
37+
alt_names = bundle.obj.municipalityname_set.all()
38+
for an in alt_names:
39+
bundle.data['name_%s' % an.language] = an.name
40+
return bundle
41+
def determine_format(self, request):
42+
if request.GET.get('format') == 'geojson':
43+
return 'application/json'
44+
return super(MunicipalityResource, self).determine_format(request)
45+
class Meta:
46+
queryset = Municipality.objects.all().order_by('name').select_related('municipalityboundary')
47+
resource_name = 'municipality'
48+
49+
class MunicipalityBoundaryResource(GeometryModelResource):
50+
municipality = fields.ToOneField('geo.api.MunicipalityResource', 'municipality')
51+
class Meta:
52+
queryset = MunicipalityBoundary.objects.all()
53+
resource_name = 'municipality_boundary'
54+
filtering = {
55+
'municipality': ALL
56+
}
57+
58+
class AddressResource(GeometryModelResource):
59+
municipality = fields.ToOneField('geo.api.MunicipalityResource', 'municipality')
60+
61+
def apply_sorting(self, objects, options=None):
62+
if options and 'lon' in options and 'lat' in options:
63+
try:
64+
lat = float(options['lat'])
65+
lon = float(options['lon'])
66+
except TypeError:
67+
raise InvalidFilterError("'lon' and 'lat' need to be floats")
68+
objects = objects.distance(Point(lon, lat, srid=4326)).order_by('distance')
69+
return super(AddressResource, self).apply_sorting(objects, options)
70+
71+
def dehydrate(self, bundle):
72+
distance = getattr(bundle.obj, 'distance', None)
73+
if distance is not None:
74+
bundle.data['distance'] = distance
75+
print bundle
76+
return bundle
77+
78+
class Meta:
79+
queryset = Address.objects.all()
80+
filtering = {
81+
'municipality': ALL,
82+
'street': ALL,
83+
'number': ALL,
84+
'letter': ALL,
85+
'location': ALL,
86+
}

geo/management/__init__.py

Whitespace-only changes.

geo/management/commands/__init__.py

Whitespace-only changes.

geo/management/commands/geo_import.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# -*- coding: utf-8 -*-
2+
import os
3+
import csv
4+
5+
from django.core.management.base import BaseCommand
6+
from geo.models import *
7+
from utils.http import HttpFetcher
8+
from django.conf import settings
9+
from django import db
10+
from django.contrib.gis.gdal import DataSource, SpatialReference, CoordTransform
11+
from django.contrib.gis.geos import GEOSGeometry, MultiPolygon, Point
12+
13+
MUNI_URL = "http://tilastokeskus.fi/meta/luokitukset/kunta/001-2013/tekstitiedosto.txt"
14+
15+
class Command(BaseCommand):
16+
help = "Manage stats app"
17+
18+
def import_municipalities(self):
19+
s = self.http.open_url(MUNI_URL, "muni")
20+
# strip first 4 lines of header and any blank/empty lines at EOF
21+
count = 0
22+
for line in s.rstrip().split('\n')[4:]:
23+
dec_line = line.decode('iso8859-1').rstrip().split('\t')
24+
(muni_id, muni_name) = dec_line
25+
muni_id = int(muni_id)
26+
muni_name = muni_name.split(' - ')[0]
27+
28+
try:
29+
muni = Municipality.objects.get(id=muni_id)
30+
except:
31+
muni = Municipality(id=muni_id, name=muni_name)
32+
muni.save()
33+
count += 1
34+
print "%d municipalities added." % count
35+
36+
def import_addresses(self):
37+
f = open(os.path.join(self.data_path, 'pks_osoite.csv'))
38+
reader = csv.reader(f, delimiter=',')
39+
reader.next()
40+
muni_list = Municipality.objects.all()
41+
muni_dict = {}
42+
for muni in muni_list:
43+
muni_dict[muni.name] = muni
44+
for idx, row in enumerate(reader):
45+
street = row[0]
46+
num = int(row[1])
47+
num2 = row[2]
48+
letter = row[3]
49+
coord_n = int(row[8])
50+
coord_e = int(row[9])
51+
muni_name = row[10]
52+
row_type = int(row[-1])
53+
if row_type != 1:
54+
continue
55+
id_s = "%s %d" % (street, num)
56+
if id_s == 'Eliel Saarisen tie 4':
57+
muni_name = 'Helsinki'
58+
muni = muni_dict[muni_name]
59+
args = dict(municipality=muni, street=street, number=num, letter=letter)
60+
try:
61+
addr = Address.objects.get(**args)
62+
except Address.DoesNotExist:
63+
addr = Address(**args)
64+
# 3879
65+
to_wgs84 = CoordTransform(SpatialReference('3879'), SpatialReference('WGS84'))
66+
pnt = Point(coord_e, coord_n, srid=3879)
67+
pnt.transform(to_wgs84)
68+
#print "%s: %s %d%s N%d E%d (%f,%f)" % (muni_name, street, num, letter, coord_n, coord_e, pnt.y, pnt.x)
69+
if idx > 0 and idx % 1000 == 0:
70+
print "%d addresses processed" % idx
71+
# Reset DB query store to free up memory
72+
db.reset_queries()
73+
addr.location = pnt
74+
addr.save()
75+
76+
def handle(self, **options):
77+
http = HttpFetcher()
78+
http.set_cache_dir(os.path.join(settings.PROJECT_ROOT, ".cache"))
79+
self.data_path = os.path.join(settings.PROJECT_ROOT, '..', 'data')
80+
self.http = http
81+
print "Importing municipalities"
82+
self.import_municipalities()
83+
print "Importing addresses"
84+
self.import_addresses()

geo/models.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from django.contrib.gis.db import models
2+
3+
class Municipality(models.Model):
4+
id = models.IntegerField(primary_key=True)
5+
name = models.CharField(max_length=50)
6+
7+
def __unicode__(self):
8+
return self.name
9+
10+
# For municipality names in other languages
11+
class MunicipalityName(models.Model):
12+
municipality = models.ForeignKey(Municipality)
13+
language = models.CharField(max_length=8)
14+
name = models.CharField(max_length=50)
15+
16+
class MunicipalityBoundary(models.Model):
17+
municipality = models.OneToOneField(Municipality)
18+
borders = models.MultiPolygonField()
19+
20+
objects = models.GeoManager()
21+
22+
class Address(models.Model):
23+
street = models.CharField(max_length=50, db_index=True)
24+
number = models.PositiveIntegerField()
25+
letter = models.CharField(max_length=2, blank=True, null=True)
26+
location = models.PointField()
27+
municipality = models.ForeignKey(Municipality, db_index=True)
28+
29+
objects = models.GeoManager()
30+
31+
class Meta:
32+
unique_together = (('municipality', 'street', 'number', 'letter'),)

geo/tests.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
This file demonstrates writing tests using the unittest module. These will pass
3+
when you run "manage.py test".
4+
5+
Replace this with more appropriate tests for your application.
6+
"""
7+
8+
from django.test import TestCase
9+
10+
11+
class SimpleTest(TestCase):
12+
def test_basic_addition(self):
13+
"""
14+
Tests that 1 + 1 always equals 2.
15+
"""
16+
self.assertEqual(1 + 1, 2)

geo/views.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Create your views here.

geocoder/__init__.py

Whitespace-only changes.

geocoder/settings.py

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import os
2+
3+
# Django settings for geocoder project.
4+
5+
DEBUG = True
6+
TEMPLATE_DEBUG = DEBUG
7+
8+
ADMINS = (
9+
# ('Your Name', '[email protected]'),
10+
)
11+
12+
MANAGERS = ADMINS
13+
14+
DATABASES = {
15+
'default': {
16+
'ENGINE': 'django.contrib.gis.db.backends.postgis', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
17+
'NAME': 'osoite', # Or path to database file if using sqlite3.
18+
# The following settings are not used with sqlite3:
19+
'USER': 'osoite',
20+
'PASSWORD': 'osoite',
21+
'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP.
22+
'PORT': '', # Set to empty string for default.
23+
}
24+
}
25+
26+
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
27+
28+
# Local time zone for this installation. Choices can be found here:
29+
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
30+
# although not all choices may be available on all operating systems.
31+
# In a Windows environment this must be set to your system time zone.
32+
TIME_ZONE = 'Europe/Helsinki'
33+
34+
# Language code for this installation. All choices can be found here:
35+
# http://www.i18nguy.com/unicode/language-identifiers.html
36+
LANGUAGE_CODE = 'fi'
37+
38+
SITE_ID = 1
39+
40+
# If you set this to False, Django will make some optimizations so as not
41+
# to load the internationalization machinery.
42+
USE_I18N = True
43+
44+
# If you set this to False, Django will not format dates, numbers and
45+
# calendars according to the current locale.
46+
USE_L10N = True
47+
48+
# If you set this to False, Django will not use timezone-aware datetimes.
49+
USE_TZ = True
50+
51+
# Absolute filesystem path to the directory that will hold user-uploaded files.
52+
# Example: "/var/www/example.com/media/"
53+
MEDIA_ROOT = ''
54+
55+
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
56+
# trailing slash.
57+
# Examples: "http://example.com/media/", "http://media.example.com/"
58+
MEDIA_URL = ''
59+
60+
# Absolute path to the directory static files should be collected to.
61+
# Don't put anything in this directory yourself; store your static files
62+
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
63+
# Example: "/var/www/example.com/static/"
64+
STATIC_ROOT = ''
65+
66+
# URL prefix for static files.
67+
# Example: "http://example.com/static/", "http://static.example.com/"
68+
STATIC_URL = '/static/'
69+
70+
# Additional locations of static files
71+
STATICFILES_DIRS = (
72+
# Put strings here, like "/home/html/static" or "C:/www/django/static".
73+
# Always use forward slashes, even on Windows.
74+
# Don't forget to use absolute paths, not relative paths.
75+
)
76+
77+
# List of finder classes that know how to find static files in
78+
# various locations.
79+
STATICFILES_FINDERS = (
80+
'django.contrib.staticfiles.finders.FileSystemFinder',
81+
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
82+
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
83+
)
84+
85+
# Make this unique, and don't share it with anybody.
86+
SECRET_KEY = 'gp_*jv(bspmp5rv-=hg9lue-$%pf!a))ch#x&(uf%a_xsj*#sy'
87+
88+
# List of callables that know how to import templates from various sources.
89+
TEMPLATE_LOADERS = (
90+
'django.template.loaders.filesystem.Loader',
91+
'django.template.loaders.app_directories.Loader',
92+
# 'django.template.loaders.eggs.Loader',
93+
)
94+
95+
MIDDLEWARE_CLASSES = (
96+
'django.middleware.common.CommonMiddleware',
97+
'django.contrib.sessions.middleware.SessionMiddleware',
98+
'django.middleware.csrf.CsrfViewMiddleware',
99+
'django.contrib.auth.middleware.AuthenticationMiddleware',
100+
'django.contrib.messages.middleware.MessageMiddleware',
101+
# Uncomment the next line for simple clickjacking protection:
102+
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
103+
)
104+
105+
ROOT_URLCONF = 'geocoder.urls'
106+
107+
# Python dotted path to the WSGI application used by Django's runserver.
108+
WSGI_APPLICATION = 'geocoder.wsgi.application'
109+
110+
TEMPLATE_DIRS = (
111+
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
112+
# Always use forward slashes, even on Windows.
113+
# Don't forget to use absolute paths, not relative paths.
114+
)
115+
116+
INSTALLED_APPS = (
117+
'django.contrib.auth',
118+
'django.contrib.contenttypes',
119+
'django.contrib.sessions',
120+
'django.contrib.sites',
121+
'django.contrib.messages',
122+
'django.contrib.staticfiles',
123+
'django.contrib.gis',
124+
# Uncomment the next line to enable the admin:
125+
# 'django.contrib.admin',
126+
# Uncomment the next line to enable admin documentation:
127+
# 'django.contrib.admindocs',
128+
'tastypie',
129+
'geo',
130+
)
131+
132+
# A sample logging configuration. The only tangible logging
133+
# performed by this configuration is to send an email to
134+
# the site admins on every HTTP 500 error when DEBUG=False.
135+
# See http://docs.djangoproject.com/en/dev/topics/logging for
136+
# more details on how to customize your logging configuration.
137+
LOGGING = {
138+
'version': 1,
139+
'disable_existing_loggers': False,
140+
'filters': {
141+
'require_debug_false': {
142+
'()': 'django.utils.log.RequireDebugFalse'
143+
}
144+
},
145+
'handlers': {
146+
'mail_admins': {
147+
'level': 'ERROR',
148+
'filters': ['require_debug_false'],
149+
'class': 'django.utils.log.AdminEmailHandler'
150+
}
151+
},
152+
'loggers': {
153+
'django.request': {
154+
'handlers': ['mail_admins'],
155+
'level': 'ERROR',
156+
'propagate': True,
157+
},
158+
}
159+
}

0 commit comments

Comments
 (0)