Sunday, August 16, 2015

Setting Up GeoDjango II

In my previous post on this topic, I ended up stuck with a database not existing error, which my good friend Kevin Cole very politely (only implying that I'm an idiot, while refraining from directly saying so ;-) pointed out that the documentation I'm using contains the instructions I need to create the database:
(env)$ createdb -T template_postgis geodjango

Unfortunately, running that gave me the following error:

createdb: database creation failed: ERROR:  template database "template_postgis" does not exist
Let me try a modified version (modified because my user doesn't have the privileges needed to create extensions) of the steps laid out here:
(env)$ createdb geodjango
(env)$ sudo su - postgres
$ psql geodjango
psql (9.3.9)
Type "help" for help.

geodjango=# CREATE EXTENSION postgis;
CREATE EXTENSION
geodjango=# \q
$ exit
logout
(env)$
Now let me resume where I left off before the error:
(env)$ python manage.py sqlmigrate world 0001
BEGIN;
CREATE TABLE "world_worldborder" ("id" serial NOT NULL PRIMARY KEY, "name" varchar(50) NOT NULL, "area" integer NOT NULL, "pop2005" integer NOT NULL, "fips" varchar(2) NOT NULL, "iso2" varchar(2) NOT NULL, "iso3" varchar(3) NOT NULL, "un" integer NOT NULL, "region" integer NOT NULL, "subregion" integer NOT NULL, "lon" double precision NOT NULL, "lat" double precision NOT NULL, "mpoly" geometry(MULTIPOLYGON,4326) NOT NULL);
CREATE INDEX "world_worldborder_mpoly_id" ON "world_worldborder" USING GIST ("mpoly" );

COMMIT;
(env)$ python manage.py migrate
Operations to perform:
  Synchronize unmigrated apps: gis, messages, staticfiles
  Apply all migrations: world, admin, contenttypes, auth, sessions
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying sessions.0001_initial... OK
  Applying world.0001_initial... OK
(env)$
Progress!  Let me keep going and see if my good fortune holds:
(env)$ python manage.py shell
Python 3.4.0 (default, Jun 19 2015, 14:20:21)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> import os
>>> import world
>>> world_shp = os.path.abspath(os.path.join(os.path.dirname(world.__file__),
...                             'data/TM_WORLD_BORDERS-0.3.shp'))
>>> from django.contrib.gis.gdal import DataSource
>>> ds = DataSource(world_shp)
>>> print(ds)
/home/[user/geodjango/geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile)
>>> print(len(ds))
1
>>> lyr = ds[0]
>>> print(lyr)
TM_WORLD_BORDERS-0.3
>>> print(lyr.geom_type)
Polygon
>>> print(len(lyr))
246
>>>
The tutorial continues with several other interactive examples showing how to use GeoDjango's pythonic interface to the GDAL library.  I began experimenting with python's GDAL wrapper back in April as part of the Introduction to GIS Programming and Algorithms course I took at George Mason University.  I documented the installation of these tools in a post at that time.  The ability to "play" with data at run time is one of the many things I love about Python, and this tutorial, like most python tutorials, is making good use of that powerful pedagogical feature of the language.  There is no need for me to recount the other examples here, however, so I'll skip over them.

The next step in the tutorial is to create a file in the world app named load.py that contains the following:
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):
    lm = LayerMapping(WorldBorder, world_shp, world_mapping,
                      transform=False, encoding='iso-8859-1')

    lm.save(strict=True, verbose=verbose)
Note: The tutorial lists: from models import WorldBorder, which will cause an import error.  models needs to be .models for this to work.

After making that change, I was able to:
(env) python manage.py shell
>>> from world import load
>>> load.run()
and watch as the countries of the world were loaded into the database.

Creating the github repo

Now would a good time to create a github repo.  First, I'll create a .gitignore file inside the top level geodjango directory (where manage.py is located) that lists the things I don't want in the repository:
env/
__pycache__/
*.py[cod]
*.sw?
This will tell git not to include the virtual environment, the python byte code files, and any vim swap files.  Next install git and initialize the repository:
(env)$ sudo aptititude install git
(env)$ git init
Now I'll check to see what a git add . would add:
(env)$ git add -n .
Since it looked good, I'll add do it, after configuring my git email and user:
(env)$ git config --global user.email [github email address]
(env)$ git config --global user.name [github name]
(env)$ git add .
(env)$ git commit -a
Initial commit.
Now push it to github (after adding an ssh key and creating a learn_geodjango project on github):
git remote add origin git@github.com:[user]/learn_geodjango.git
git push -u origin master
With the github repo now created, I'll continue the tutorial in a future post.



No comments:

Post a Comment