Skip to content

Commit

Permalink
Project country determination
Browse files Browse the repository at this point in the history
 - Determine project country from extent geometry using world country
   boundary data.
 - Set up country boundary data during provisioning.
  • Loading branch information
Ian Ross committed Apr 18, 2016
1 parent 15a742e commit 1b602ae
Show file tree
Hide file tree
Showing 16 changed files with 182 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ omit =
cadasta/*/__init__.py
cadasta/config/*
cadasta/manage.py
cadasta/core/management/commands/*
cadasta/*/management/commands/*
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ vagrant.retry
/.tox/
/cadasta_platform.egg-info/
/pytest.txt

/cadasta/geography/data
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ before_script:
- psql -c 'create database cadasta;' -U postgres
- psql -U postgres -d cadasta -c "create extension postgis;"
- python cadasta/manage.py migrate
- mkdir cadasta/geography/data
- export WBDATA=TM_WORLD_BORDERS-0.3.zip
- export DATADIR=cadasta/geography/data
- wget -O $DATADIR/$WBDATA http://thematicmapping.org/downloads/$WBDATA
- unzip $DATADIR/$WBDATA -d $DATADIR

env:
matrix:
Expand Down
1 change: 1 addition & 0 deletions cadasta/config/settings/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
'corsheaders',

'core',
'geography',
'accounts',
'organization',

Expand Down
Empty file added cadasta/geography/__init__.py
Empty file.
30 changes: 30 additions & 0 deletions cadasta/geography/load.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import os
from django.contrib.gis.utils import LayerMapping
from .models import WorldBorder


world_mapping = {
'fips': 'FIPS',
'iso2': 'ISO2',
'iso3': 'ISO3',
'un': 'UN',
'name': 'NAME',
'area': 'AREA',
'pop2005': 'POP2005',
'region': 'REGION',
'subregion': 'SUBREGION',
'lon': 'LON',
'lat': 'LAT',
'mpoly': 'MULTIPOLYGON',
}

world_shp = os.path.abspath(os.path.join(os.path.dirname(__file__),
'data', 'TM_WORLD_BORDERS-0.3.shp'))


def run(verbose=True):
WorldBorder.objects.all().delete()
lm = LayerMapping(WorldBorder, world_shp, world_mapping,
transform=False, encoding='iso-8859-1')

lm.save(strict=True, verbose=verbose)
Empty file.
Empty file.
10 changes: 10 additions & 0 deletions cadasta/geography/management/commands/loadcountries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.core.management.base import BaseCommand

from ... import load


class Command(BaseCommand):
help = """Loads country boundary data."""

def handle(self, *args, **options):
load.run()
35 changes: 35 additions & 0 deletions cadasta/geography/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-18 20:24
from __future__ import unicode_literals

import django.contrib.gis.db.models.fields
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='WorldBorder',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
('area', models.IntegerField()),
('pop2005', models.IntegerField(verbose_name='Population 2005')),
('fips', models.CharField(max_length=2, verbose_name='FIPS Code')),
('iso2', models.CharField(max_length=2, verbose_name='2 Digit ISO')),
('iso3', models.CharField(max_length=3, verbose_name='3 Digit ISO')),
('un', models.IntegerField(verbose_name='United Nations Code')),
('region', models.IntegerField(verbose_name='Region Code')),
('subregion', models.IntegerField(verbose_name='Sub-Region Code')),
('lon', models.FloatField()),
('lat', models.FloatField()),
('mpoly', django.contrib.gis.db.models.fields.MultiPolygonField(srid=4326)),
],
),
]
Empty file.
19 changes: 19 additions & 0 deletions cadasta/geography/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.contrib.gis.db import models


class WorldBorder(models.Model):
name = models.CharField(max_length=50)
area = models.IntegerField()
pop2005 = models.IntegerField('Population 2005')
fips = models.CharField('FIPS Code', max_length=2)
iso2 = models.CharField('2 Digit ISO', max_length=2)
iso3 = models.CharField('3 Digit ISO', max_length=3)
un = models.IntegerField('United Nations Code')
region = models.IntegerField('Region Code')
subregion = models.IntegerField('Sub-Region Code')
lon = models.FloatField()
lat = models.FloatField()
mpoly = models.MultiPolygonField()

def __str__(self):
return self.name
13 changes: 13 additions & 0 deletions cadasta/geography/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.test import TestCase

from geography.models import WorldBorder


class WorldBorderTest(TestCase):
def test_str(self):
border = WorldBorder.objects.create(
name='Narnia', area=1, pop2005=1, un=1, region=1, subregion=1,
lat=0, lon=0,
mpoly='MULTIPOLYGON(((10 10, 10 20, 20 20, 20 15, 10 10)))'
)
assert str(border) == 'Narnia'
12 changes: 12 additions & 0 deletions cadasta/organization/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from tutelary.models import Policy

from core.models import RandomIDModel
from geography.models import WorldBorder
from .validators import validate_contact
from .choices import ROLE_CHOICES
from . import messages
Expand Down Expand Up @@ -173,6 +174,17 @@ class TutelaryMeta:
def __str__(self):
return "<Project: {name}>".format(name=self.name)

def save(self, *args, **kwargs):
if ((self.country is None or self.country == '') and
self.extent is not None):
try:
self.country = WorldBorder.objects.get(
mpoly__contains=self.extent.centroid
).iso2
except:
pass
super().save(*args, **kwargs)


class ProjectRole(RandomIDModel):
project = models.ForeignKey(Project)
Expand Down
29 changes: 27 additions & 2 deletions cadasta/organization/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from tutelary.models import Policy

from accounts.tests.factories import UserFactory
from geography import load as load_countries
from .factories import OrganizationFactory, ProjectFactory
from ..models import OrganizationRole, ProjectRole

Expand All @@ -12,7 +13,7 @@

class OrganizationTest(TestCase):
def test_str(self):
org = OrganizationFactory.create(**{'name': 'Org'})
org = OrganizationFactory.create(name='Org')
assert str(org) == '<Organization: Org>'

def test_has_random_id(self):
Expand Down Expand Up @@ -75,13 +76,37 @@ def test_delete_project_roles(self):

class ProjectTest(TestCase):
def test_str(self):
project = ProjectFactory.create(**{'name': 'Project'})
project = ProjectFactory.create(name='Project')
assert str(project) == '<Project: Project>'

def test_has_random_id(self):
project = ProjectFactory.create()
assert type(project.id) is not int

def test_country_assignment(self):
load_countries.run()
project = ProjectFactory.create(
extent='SRID=4326;POLYGON(('
'11.36667 47.25000, '
'11.41667 47.25000, '
'11.41667 47.28333, '
'11.36667 47.28333, '
'11.36667 47.25000))'
)
assert project.country == 'AT'

def test_country_assignment_for_invalid_geometry(self):
load_countries.run()
project = ProjectFactory.create(
extent='SRID=4326;POLYGON(('
'0.00000 0.00000, '
'0.00001 0.00000, '
'0.00001 0.00001, '
'0.00000 0.00001, '
'0.00000 0.00000))'
)
assert project.country == ''


class ProjectRoleTest(TestCase):
def setUp(self):
Expand Down
27 changes: 27 additions & 0 deletions provision/roles/cadasta/application/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- python3.5-dev
- python-virtualenv
- git
- unzip

- name: Create base directory for virtual environment
become: yes
Expand Down Expand Up @@ -41,6 +42,24 @@
become_user: root
file: path=/var/log/django state=directory owner="{{ app_user }}"

- name: Create temporary data processing directory
become: yes
become_user: "{{ app_user }}"
file: path="{{ application_path }}cadasta/geography/data" state=directory

- name: Download world boundary data files
become: yes
become_user: "{{ app_user }}"
get_url: url=http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
dest="{{ application_path }}cadasta/geography/data"

- name: Unzip world boundary data files
become: yes
become_user: "{{ app_user }}"
unarchive: creates="{{ application_path }}cadasta/geography/data/TM_WORLD_BORDERS-0.3.shp"
src="{{ application_path }}cadasta/geography/data/TM_WORLD_BORDERS-0.3.zip"
dest="{{ application_path }}cadasta/geography/data" copy=no

- name: Django migrate
become: yes
become_user: "{{ app_user }}"
Expand All @@ -49,6 +68,14 @@
virtualenv="{{ virtualenv_path }}"
settings="{{ django_settings }}"

- name: Load country boundary data
become: yes
become_user: "{{ app_user }}"
django_manage: command=loadcountries
app_path="{{ application_path }}cadasta"
virtualenv="{{ virtualenv_path }}"
settings="{{ django_settings }}"

- name: Install Bootstrap for SASS processing
become: yes
become_user: "{{ app_user }}"
Expand Down

0 comments on commit 1b602ae

Please sign in to comment.