Skip to content

Commit

Permalink
Merge branch 'develop' into 'master'
Browse files Browse the repository at this point in the history
Develop

See merge request !2
  • Loading branch information
gubuntu committed Mar 30, 2017
2 parents e8b2722 + b421104 commit 5ae04d6
Show file tree
Hide file tree
Showing 20 changed files with 2,260 additions and 1,074 deletions.
Binary file modified DMS2DD.ods
Binary file not shown.
1,154 changes: 723 additions & 431 deletions PyQt4Dialogs.py

Large diffs are not rendered by default.

32 changes: 0 additions & 32 deletions README.html

This file was deleted.

54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# CoGo plugin for QGIS

First developed by Afrispatial cc in 2012

Sponsored by: SpatialMatrix, Lagos for Ogun State Government, Nigeria.

Original authors: Robert Moerman, Admire Nyakudya, Gavin Fleming

Maintained by Kartoza (Pty) Ltd

Copyright Kartoza 2017

2017 update for Niger State, Nigeria and potentially for general release.

Licence:

# Description

This plugin was developed to enable efficient bulk capture of coordinate geometry off survey diagrams.

It caters for addition, modification and deletion of cadastral properties.

## Bearings and distances

Where bearing and distance data are available, they can and should be used to defined property boundaries. An initial beacon coordinate is captured and then bearings and distances are captured to define all other beacons in a chain.

Then properties are constructed by grouping beacons in a specific order to define a polygon.

## Coordinates

Where bearings and distances are not available, coordinates can be used instead to define beacons.

## Dependencies

This plugin depends on a PostGIS database with a predefined schema including tables, materialised views and triggers.

# User manual

http://goo.gl/CY9TYn


# What's Next
(Robert's old note - needs to be updated)

- Copy the entire directory containing your new plugin to the QGIS plugin directory
- Compile the ui file using pyuic4
- Compile the resources file using pyrcc4
- Test the plugin by enabling it in the QGIS plugin manager
- Customize it by editing the implementation file `sml_surveyor.py`
- Create your own custom icon, replacing the default `icon.png`
- Modify your user interface by opening `sml_surveyor.ui` in Qt Designer (don't forget to compile it with pyuic4 after changing it)
- You can use the `Makefile` to compile your Ui and resource files when you make changes. This requires GNU make (gmake)


34 changes: 0 additions & 34 deletions README.txt

This file was deleted.

8 changes: 2 additions & 6 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,17 @@
def name():
return "SML Surveyor"


def description():
return "SML Surveyor Plugin"


def version():
return "Version 0.1"


def icon():
return "icon.png"


def qgisMinimumVersion():
return "1.8"
return "2.0"

def author():
return "AfriSpatial"
Expand All @@ -49,5 +45,5 @@ def email():

def classFactory(iface):
# load sml_surveyor class from file sml_surveyor
from sml_surveyor import sml_surveyor
from plugin import sml_surveyor
return sml_surveyor(iface)
54 changes: 54 additions & 0 deletions constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
SQL_BEACONS = {
"SELECT":"SELECT beacon FROM beacons WHERE gid = %s;",
"UNIQUE":"SELECT COUNT(*) FROM beacons WHERE %s = %s;",
"EDIT":"SELECT {fields} FROM beacons WHERE gid = %s;",
"DELETE":"DELETE FROM beacons WHERE gid = %s;",
"INSERT":"INSERT INTO beacons({fields}) VALUES ({values}) RETURNING gid;",
"UPDATE":"UPDATE beacons SET {set} WHERE {where};",
"BEARDIST":"SELECT CASE WHEN count(*) = 0 THEN FALSE ELSE TRUE END \
FROM beardist WHERE beacon_to = (SELECT beacon FROM \
beacons WHERE gid = %s);",
}

SQL_PARCELS = {
"SELECT":"SELECT parcel_id FROM parcel_lookup WHERE parcel_id = %s;",
"EDIT":"SELECT l.parcel_id, array_agg(s.gid ORDER BY s.sequence) \
FROM ( SELECT b.gid, d.parcel_id, d.sequence FROM beacons b \
INNER JOIN parcel_def d ON d.beacon = b.beacon) s JOIN \
parcel_lookup l ON s.parcel_id = l.parcel_id WHERE \
l.parcel_id = %s GROUP BY l.parcel_id;",
"AUTOCOMPLETE":"SELECT parcel_id FROM parcel_lookup WHERE available;",
"UNIQUE":"SELECT COUNT(*) FROM parcel_lookup WHERE parcel_id = %s;",
"AVAILABLE":"SELECT available FROM parcel_lookup WHERE parcel_id = %s;",
"INSERT":"INSERT INTO parcel_def(parcel_id, beacon, sequence) \
VALUES (%s, %s, %s);",
"INSERT_GENERAL": "INSERT INTO parcel_def(parcel_id, beacon, sequence) \
VALUES %s;",
"DELETE":"DELETE FROM parcel_def WHERE parcel_id = %s;",
}

SQL_BEARDIST = {
"AUTO_SURVEYPLAN":"SELECT array_agg(plan_no) FROM survey;",
"AUTO_REFERENCEBEACON":"SELECT array_agg(beacon) FROM beacons \
WHERE beacon NOT IN (SELECT beacon_to FROM beardist WHERE \
beacon_to NOT IN (SELECT ref_beacon FROM survey));",
"EXIST_REFERENCEBEACON":"SELECT ref_beacon FROM survey where \
plan_no = %s;",
"EXIST_BEARDISTCHAINS":"SELECT bd.bearing, bd.distance, \
bd.beacon_from, bd.beacon_to, b.location, b.name FROM beardist \
bd INNER JOIN beacons b ON bd.beacon_to = b.beacon WHERE \
bd.plan_no = %s;",
"INDEX_REFERENCEBEACON":"SELECT i.column_index::integer FROM (SELECT \
row_number() over(ORDER BY c.ordinal_position) -1 as \
column_index, c.column_name FROM information_schema.columns c \
WHERE c.table_name = 'beacons' AND c.column_name NOT IN ('geom', \
'gid') ORDER BY c.ordinal_position) as i WHERE i.column_name = \
'beacon';",
"IS_SURVEYPLAN":"SELECT CASE WHEN COUNT(*) <> 0 THEN TRUE ELSE FALSE \
END FROM survey WHERE plan_no = %s;",
"INSERT_SURVEYPLAN":"INSERT INTO survey(plan_no, ref_beacon) \
VALUES(%s, %s);",
"UPDATE_LINK":"SELECT beardistupdate(%s, %s, %s, %s, %s, %s, %s, %s);",
"DELETE_LINK":"DELETE FROM beacons WHERE beacon = %s;",
"INSERT_LINK":"SELECT beardistinsert(%s, %s, %s, %s, %s, %s, %s);"
}
58 changes: 40 additions & 18 deletions database.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,20 @@
* *
***************************************************************************/
"""

import psycopg2

class manager():


class Field:

def __init__(self, name, type, required, unique):
self.name = name
self.type = type
self.required = required
self.unique = unique


class Manager:

def __init__(self, params):
# test db settings
self.params = params
Expand All @@ -30,25 +39,25 @@ def connect(self, params):
""" Create a backend postgres database connection
"""
try:
# check if connection object exist
# check if connection object exist
if not hasattr(self, 'conn') or self.conn is None:
self.conn = psycopg2.connect("host='{HOST}' dbname='{NAME}' user='{USER}' password='{PASSWORD}' port='{PORT}'".format(HOST=params["HOST"], NAME=params["NAME"], USER=params["USER"], PASSWORD= params["PASSWORD"], PORT=params["PORT"]))
# check if cursor objet exists
if not hasattr(self, 'cursor') or self.cursor is None:
self.cursor = self.conn.cursor()
except Exception as e:
raise Exception('Could not connect to database!\nError raised: {error}.'.format(error = str(e)))

def disconnect(self):
""" Terminate a backend postgres database connection
"""
try:
# check if a cursor object exists
if hasattr(self, 'cursor') and self.cursor is not None:
# check if a cursor object exists
if hasattr(self, 'cursor') and self.cursor is not None:
self.cursor.close()
self.cursor = None
# check if a connection object exists
if hasattr(self, 'conn') and self.conn is not None:
if hasattr(self, 'conn') and self.conn is not None:
self.conn.close()
self.conn = None
except Exception as e:
Expand All @@ -60,39 +69,52 @@ def query(self, query, data=None):
"""
try:
self.connect(self.params)
if data is None: self.cursor.execute(query)
else: self.cursor.execute(query, data)
if data is None:
self.cursor.execute(query)
else:
self.cursor.execute(query, data)
records = None
try:
records = self.cursor.fetchall()
except:
except:
pass
self.conn.commit()
self.disconnect()
return records
except Exception as e:
raise Exception('Backend database query failed!\nError raised: %s.' %(str(e),))

def queryPreview(self, query, data=None):
def queryPreview(self, query, data=None, multi_data=False):
""" Preview query
@returns query (str)
"""
try:
self.connect(self.params)
sql = ""
if data is None: sql = self.cursor.mogrify(query)
else: sql = self.cursor.mogrify(query, data)
if data is None:
sql = self.cursor.mogrify(query)
else:
if multi_data:
placeholders = ','.join(['%s' for dummy in data])
query = query % (placeholders)
sql = self.cursor.mogrify(query, data)
else:
sql = self.cursor.mogrify(query, data)
self.disconnect()
return sql
except Exception as e:
raise Exception('Backend database mogrification failed!\nError raised: %s.' %(str(e),))

def getSchema(self, tbl_name, fld_ignore):
""" Get information abot a specific table
""" Get information abot a specific table
@returns [<Field Name>, <Field Type>, <Nullable>] (list)
"""
info = [{"NAME":data[0], "TYPE":self._pythonize_type(data[1]), "REQUIRED":data[2], "UNIQUE":data[3]} for data in reversed(self.query("SELECT c.column_name, c.data_type, CASE WHEN c.is_nullable = 'NO' THEN TRUE ELSE FALSE END AS required, CASE WHEN u.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS unique FROM information_schema.columns c LEFT JOIN (SELECT kcu.column_name, tc.table_name FROM information_schema.table_constraints tc LEFT JOIN information_schema.key_column_usage kcu ON tc.constraint_catalog = kcu.constraint_catalog AND tc.constraint_schema = kcu.constraint_schema AND tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') AND tc.table_name = '{table}') u ON u.column_name = c.column_name WHERE c.table_name = '{table}' AND c.column_name NOT IN ({ignore});".format(table = tbl_name, ignore = ", ".join("'%s'" %(i,) for i in fld_ignore))))]
return info
return [Field(
data[0],
self._pythonize_type(data[1]),
data[2],
data[3]
) for data in reversed(self.query("SELECT c.column_name, c.data_type, CASE WHEN c.is_nullable = 'NO' THEN TRUE ELSE FALSE END AS required, CASE WHEN u.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS unique FROM information_schema.columns c LEFT JOIN (SELECT kcu.column_name, tc.table_name FROM information_schema.table_constraints tc LEFT JOIN information_schema.key_column_usage kcu ON tc.constraint_catalog = kcu.constraint_catalog AND tc.constraint_schema = kcu.constraint_schema AND tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type IN ('UNIQUE', 'PRIMARY KEY') AND tc.table_name = '{table}') u ON u.column_name = c.column_name WHERE c.table_name = '{table}' AND c.column_name NOT IN ({ignore});".format(table = tbl_name, ignore = ", ".join("'%s'" %(i,) for i in fld_ignore))))]

def _pythonize_type(self, db_type):
""" Get python type
Expand Down
7 changes: 6 additions & 1 deletion database_changes_v2.sql
Original file line number Diff line number Diff line change
Expand Up @@ -244,4 +244,9 @@ drop view parcels;

alter table parcel_lookup alter column plot_sn type character varying;

CREATE VIEW parcels ...
CREATE VIEW parcels ...

--power cut corrupted parcel view? some parcels defined by < 3 beacons in parcel_def. So to remove them:

delete from parcel_def where parcel_id in
(select parcel_id from parcel_def group by parcel_id having count(parcel_id) <3)
35 changes: 5 additions & 30 deletions metadata.txt
Original file line number Diff line number Diff line change
@@ -1,38 +1,13 @@
# This file contains metadata for your plugin. Beginning
# with version 1.8 this is the preferred way to supply information about a
# plugin. The current method of embedding metadata in __init__.py will
# be supported until version 2.0

# This file should be included when you package your plugin.

# Mandatory items:

; Mandatory items:

[general]
name=SML Surveyor Plugin
qgisMinimumVersion=1.8
description=SML Surveyor Plugin
version=0.1
qgisMinimumVersion=2.0
version=Version 0.2
author=AfriSpatial
[email protected]

# end of mandatory metadata

# Optional items:

# Uncomment the following line and add your changelog entries:
# changelog=

# tags are comma separated with spaces allowed
tags=survey

homepage=
tracker=
repository=
icon=icon.png
# experimental flag
; Optional items:
experimental=False

# deprecated flag (applies to the whole plugin, not just a single version
deprecated=False

icon=icon.png
Loading

0 comments on commit 5ae04d6

Please sign in to comment.