| 1 | """provides the unified data store between redland RDF and spatial (for now PostGIS) index""" | 1 | """provides the unified data store between redland RDF and spatial (for now PostGIS) index""" |
|---|
| 2 | import pgdb | 2 | import pgdb |
|---|
| 3 | import re | 3 | import re |
|---|
| 4 | from warnings import warn | 4 | from warnings import warn |
|---|
| 5 | | 5 | |
|---|
| 6 | # Spatial Reference ID for WGS84 lat/long is 4326 | 6 | # Spatial Reference ID for WGS84 lat/long is 4326 |
|---|
| 7 | | 7 | |
|---|
| 8 | _srid = str(4326) | 8 | _srid = str(4326) |
|---|
| 9 | | 9 | |
|---|
| 10 | class SpatialStore: | 10 | class SpatialStore: |
|---|
| 11 | | 11 | |
|---|
| 12 | def __init__(self,database,vars=None,model=None): | 12 | def __init__(self,database,vars=None,model=None): |
|---|
| 13 | """Create a new store interface to a database name.""" | 13 | """Create a new store interface to a database name.""" |
|---|
| 14 | self.vars = vars | 14 | self.vars = vars |
|---|
| 15 | self.model = model | 15 | self.model = model |
|---|
| 16 | | 16 | |
|---|
| 17 | if database is None: | 17 | if database is None: |
|---|
| 18 | return AttributeError | 18 | return AttributeError |
|---|
| 19 | | 19 | |
|---|
| 20 | db = pgdb.connect(database=database) | 20 | db = pgdb.connect(database=database) |
|---|
| 21 | self.db = db | 21 | self.db = db |
|---|
| 22 | | 22 | |
|---|
| 23 | def parse_points(self,line): | 23 | def parse_points(self,line): |
|---|
| 24 | """Given a string of space and comma separated points, | 24 | """Given a string of space and comma separated points, |
|---|
| 25 | got usually from GeoRDF, return a list of lists. | 25 | got usually from GeoRDF, return a list of lists. |
|---|
| 26 | | 26 | |
|---|
| 27 | This could optionally support different line formats. | 27 | This could optionally support different line formats. |
|---|
| 28 | (e.g. WKT, or http://brainoff.com/worldkit/doc/polygon.php ) | 28 | (e.g. WKT, or http://brainoff.com/worldkit/doc/polygon.php ) |
|---|
| 29 | """ | 29 | """ |
|---|
| 30 | sets = line.split(',') | 30 | sets = line.split(',') |
|---|
| 31 | points = [] | 31 | points = [] |
|---|
| 32 | for s in sets: | 32 | for s in sets: |
|---|
| 33 | s = re.sub('^ ','',s) | 33 | s = re.sub('^ ','',s) |
|---|
| 34 | p = s.split(' ') | 34 | p = s.split(' ') |
|---|
| 35 | points.append(p) | 35 | points.append(p) |
|---|
| 36 | | 36 | |
|---|
| 37 | return points | 37 | return points |
|---|
| 38 | | 38 | |
|---|
| 39 | def wkt_from_points(self,points): | 39 | def wkt_from_points(self,points): |
|---|
| 40 | sets = [] | 40 | sets = [] |
|---|
| 41 | for p in points: | 41 | for p in points: |
|---|
| 42 | set = ' '.join(p) | 42 | set = ' '.join(p) |
|---|
| 43 | sets.append(set) | 43 | sets.append(set) |
|---|
| 44 | return ','.join(sets) | 44 | return ','.join(sets) |
|---|
| 45 | | 45 | |
|---|
| 46 | def geom(self,uri): | 46 | def geom(self,uri): |
|---|
| 47 | """Passed the uri of an object, or an rdfobj Object, returns geometry for it as a dict; if the geom is a point, {'x':x,'y':y}; if the geom is a line, x1,y1,x2,y2; polygon geom isn't really supported yet.""" | 47 | """Passed the uri of an object, or an rdfobj Object, returns geometry for it as a dict; if the geom is a point, {'x':x,'y':y}; if the geom is a line, x1,y1,x2,y2; polygon geom isn't really supported yet.""" |
|---|
| 48 | # again we have an issue of what kind of geom this is | 48 | # again we have an issue of what kind of geom this is |
|---|
| 49 | select = "select GeometryType(geom) from nodes where node='"+uri+"'" | 49 | select = "select GeometryType(geom) from nodes where node='"+uri+"'" |
|---|
| 50 | db = self.db.cursor() | 50 | db = self.db.cursor() |
|---|
| 51 | db.execute(select) | 51 | db.execute(select) |
|---|
| 52 | g = db.fetchone() | 52 | g = db.fetchone() |
|---|
| 53 | geom = {} | 53 | geom = {} |
|---|
| 54 | if g[0] == 'LINESTRING': | 54 | if g[0] == 'LINESTRING': |
|---|
| 55 | select = "SELECT name, type, X(StartPoint(geom)) as x, Y(StartPoint(geom)), GeomFromText(geom) as y, X(EndPoint(geom)), Y(EndPoint(geom)) from nodes where node='"+str(uri)+"'" | 55 | select = "SELECT name, type, X(StartPoint(geom)) as x, Y(StartPoint(geom)), GeomFromText(geom) as y, X(EndPoint(geom)), Y(EndPoint(geom)) from nodes where node='"+str(uri)+"'" |
|---|
| 56 | db.execute(select) | 56 | db.execute(select) |
|---|
| 57 | c = db.fetchone() | 57 | c = db.fetchone() |
|---|
| 58 | geom['x1'] = c[2] | 58 | geom['x1'] = c[2] |
|---|
| 59 | geom['x2'] = c[4] | 59 | geom['x2'] = c[4] |
|---|
| 60 | geom['y1'] = c[3] | 60 | geom['y1'] = c[3] |
|---|
| 61 | geom['y2'] = c[5] | 61 | geom['y2'] = c[5] |
|---|
| 62 | | 62 | |
|---|
| 63 | elif g[0] == 'POINT': | 63 | elif g[0] == 'POINT': |
|---|
| 64 | select = "SELECT name,type,X(geom),Y(geom) from nodes where node='"+uri+"'" | 64 | select = "SELECT name,type,X(geom),Y(geom) from nodes where node='"+uri+"'" |
|---|
| 65 | db.execute(select) | 65 | db.execute(select) |
|---|
| 66 | c = db.fetchone() | 66 | c = db.fetchone() |
|---|
| 67 | geom['x'] = c[2] | 67 | geom['x'] = c[2] |
|---|
| 68 | geom['y'] = c[3] | 68 | geom['y'] = c[3] |
|---|
| 69 | | 69 | |
|---|
| 70 | elif g[0] == 'POLYGON': | 70 | elif g[0] == 'POLYGON': |
|---|
| 71 | #@@TODO support this properly, as vectors, whatever | 71 | #@@TODO support this properly, as vectors, whatever |
|---|
| 72 | select = 'SELECT GeomAsText(geom) where node = \''+uri+"'" | 72 | select = 'SELECT GeomAsText(geom) where node = \''+uri+"'" |
|---|
| 73 | | 73 | |
|---|
| 74 | return geom | 74 | return geom |
|---|
| 75 | | 75 | |
|---|
| 76 | | 76 | |
|---|
| 77 | | 77 | |
|---|
| 78 | def find_near(self,node=None,x=None,y=None,r=None,type=None,terse=None): | 78 | def find_near(self,node=None,x=None,y=None,r=None,type=None,terse=None): |
|---|
| 79 | if r is None: | 79 | if r is None: |
|---|
| 80 | r = 0.1 | 80 | r = 0.1 |
|---|
| 81 | found = self.within_box(minx=x-r,miny=y-r,maxx=x+r,maxy=y+r,type=type) | 81 | found = self.within_box(minx=x-r,miny=y-r,maxx=x+r,maxy=y+r,type=type) |
|---|
| 82 | return found | 82 | return found |
|---|
| 83 | | 83 | |
|---|
| 154 | def add_geom(self,geom,uri,type=None,owner=None,points=[],object=None,date=None,name=None): | 177 | def add_geom(self,geom,uri,type=None,owner=None,points=[],object=None,date=None,name=None): |
|---|
| 155 | """ Add geometry for a Uri or rdfobj Object into a spatial index. name=[name] REALLY SHOULD be supplied.""" | 178 | """ Add geometry for a Uri or rdfobj Object into a spatial index. name=[name] REALLY SHOULD be supplied.""" |
|---|
| 156 | if type is None: | 179 | if type is None: |
|---|
| 157 | if object is not None: | 180 | if object is not None: |
|---|
| 158 | if object.rdf_type is not None: | 181 | if object.rdf_type is not None: |
|---|
| 159 | type = str(object.rdf_type) | 182 | type = str(object.rdf_type) |
|---|
| 160 | | 183 | |
|---|
| 161 | if type is None: | 184 | if type is None: |
|---|
| 162 | type = 'http://www.w3.org/2003/01/geo/wgs84_pos#SpatialThing' | 185 | type = 'http://www.w3.org/2003/01/geo/wgs84_pos#SpatialThing' |
|---|
| 163 | | 186 | |
|---|
| 164 | if owner is None: owner = 'wl@frot.org' | 187 | if owner is None: owner = 'wl@frot.org' |
|---|
| 165 | | 188 | |
|---|
| 166 | if date is None: | 189 | if date is None: |
|---|
| 167 | date = 'now()' | 190 | date = 'now()' |
|---|
| 168 | else: | 191 | else: |
|---|
| 169 | date = "'"+str(date)+"'" | 192 | date = "'"+str(date)+"'" |
|---|
| 170 | | 193 | |
|---|
| 171 | geomtext = None | 194 | geomtext = None |
|---|
| 172 | if geom == 'POINT': | 195 | if geom == 'POINT': |
|---|
| 173 | geomtext = "GeomFromText('POINT("+str(points[0][0])+' '+str(points[0][1])+")',4326)" | 196 | geomtext = "GeomFromText('POINT("+str(points[0][0])+' '+str(points[0][1])+")',4326)" |
|---|
| 174 | elif geom == 'LINESTRING': | 197 | elif geom == 'LINESTRING': |
|---|
| 175 | geomtext = "GeomFromText('LINESTRING("+str(points[0][0])+' '+str(points[0][1])+','+str(points[1][0])+' '+str(points[1][1])+")',4326)" | 198 | geomtext = "GeomFromText('LINESTRING("+str(points[0][0])+' '+str(points[0][1])+','+str(points[1][0])+' '+str(points[1][1])+")',4326)" |
|---|
| 176 | elif geom == 'POLYGON': | 199 | elif geom == 'POLYGON': |
|---|
| 177 | poly = [] | 200 | poly = [] |
|---|
| 178 | for p in points: | 201 | for p in points: |
|---|
| 179 | poly.append(str(p[0])+' '+str(p[1])) | 202 | poly.append(str(p[0])+' '+str(p[1])) |
|---|
| 180 | polystring = ','.join(poly) | 203 | polystring = ','.join(poly) |
|---|
| 181 | geomtext = "GeomFromText('POLYGON(("+polystring+"))',4326)" | 204 | geomtext = "GeomFromText('POLYGON(("+polystring+"))',4326)" |
|---|
| 182 | | 205 | |
|---|
| 183 | db = self.db | 206 | db = self.db |
|---|
| 184 | db.cursor().execute("INSERT INTO nodes (node,name,geom,type,created) values (%s,%s,"+geomtext+",%s,"+date+")", (str(uri),str(name),str(type))) | 207 | db.cursor().execute("INSERT INTO nodes (node,name,geom,type,created) values (%s,%s,"+geomtext+",%s,"+date+")", (str(uri),str(name),str(type))) |
|---|
| 185 | db.commit() | 208 | db.commit() |
|---|
| 186 | | 209 | |
|---|
| 187 | | 210 | |
|---|
| 188 | def update_geom(self,geom,uri,points=None,object=None,type=None,owner=None,name=None): | 211 | def update_geom(self,geom,uri,points=None,object=None,type=None,owner=None,name=None): |
|---|
| 189 | | 212 | |
|---|
| 190 | """Given a geometry type ('POINT','LINESTRING' or 'POLYGON'), and a uri, replace | 213 | """Given a geometry type ('POINT','LINESTRING' or 'POLYGON'), and a uri, replace |
|---|
| 191 | the geometry for that URI with the new one contained in the points list.""" | 214 | the geometry for that URI with the new one contained in the points list.""" |
|---|
| 192 | if geom is None: return | 215 | if geom is None: return |
|---|
| 193 | if uri is None: | 216 | if uri is None: |
|---|
| 194 | if object is not None: | 217 | if object is not None: |
|---|
| 195 | uri = str(object) | 218 | uri = str(object) |
|---|
| 196 | else: | 219 | else: |
|---|
| 197 | return | 220 | return |
|---|
| 198 | uri = uri.replace("'","\\'") | 221 | uri = uri.replace("'","\\'") |
|---|
| 199 | db = self.db | 222 | db = self.db |
|---|
| 200 | c = db.cursor() | 223 | c = db.cursor() |
|---|
| 201 | if geom == 'POINT': | 224 | if geom == 'POINT': |
|---|
| 202 | c.execute("UPDATE nodes SET geom = GeomFromText('POINT("+str(points[0][0])+' '+str(points[0][1])+")',4326) where node = '"+str(uri)+"'") | 225 | c.execute("UPDATE nodes SET geom = GeomFromText('POINT("+str(points[0][0])+' '+str(points[0][1])+")',4326) where node = '"+str(uri)+"'") |
|---|
| 203 | elif geom == 'LINESTRING': | 226 | elif geom == 'LINESTRING': |
|---|
| 204 | pass | 227 | pass |
|---|
| 205 | elif geom == 'POLYGON': | 228 | elif geom == 'POLYGON': |
|---|
| 206 | pass | 229 | pass |
|---|
| 207 | if name is not None: | 230 | if name is not None: |
|---|
| 208 | c.execute("UPDATE nodes SET name = %s WHERE node = %s",(str(name),str(uri),)) | 231 | c.execute("UPDATE nodes SET name = %s WHERE node = %s",(str(name),str(uri),)) |
|---|
| 209 | db.commit() | 232 | db.commit() |
|---|
| 210 | | 233 | |
|---|
| 211 | def geom_exists(self,uri): | 234 | def geom_exists(self,uri): |
|---|
| 212 | if uri is None: return | 235 | if uri is None: return |
|---|
| 213 | uri = str(uri) | 236 | uri = str(uri) |
|---|
| 214 | uri = re.sub("'","\\'",uri) | 237 | uri = re.sub("'","\\'",uri) |
|---|
| 215 | db = self.db | 238 | db = self.db |
|---|
| 216 | c = db.cursor() | 239 | c = db.cursor() |
|---|
| 217 | c.execute("SELECT X(geom) as x, Y(geom) as y from nodes WHERE node = '"+uri+"'") | 240 | c.execute("SELECT X(geom) as x, Y(geom) as y from nodes WHERE node = '"+uri+"'") |
|---|
| 218 | node = c.fetchone() | 241 | node = c.fetchone() |
|---|
| 219 | if node is not None: | 242 | if node is not None: |
|---|
| 220 | return True | 243 | return True |
|---|
| 221 | else: | 244 | else: |
|---|
| 222 | return False | 245 | return False |
|---|
| 223 | | 246 | |
|---|
| 224 | | 247 | |
|---|
| 225 | def add_or_update_geom(self,geom,uri,points=None,type=None,owner=None,object=None,name=None): | 248 | def add_or_update_geom(self,geom,uri,points=None,type=None,owner=None,object=None,name=None): |
|---|
| 226 | exists = self.geom_exists(uri) | 249 | exists = self.geom_exists(uri) |
|---|
| 227 | if exists is True: | 250 | if exists is True: |
|---|
| 228 | self.update_geom(geom,uri,points=points,type=type,owner=owner,object=object,name=name) | 251 | self.update_geom(geom,uri,points=points,type=type,owner=owner,object=object,name=name) |
|---|
| 229 | else: | 252 | else: |
|---|
| 230 | self.add_geom(geom,uri,points=points,type=type,owner=owner,object=object,name=name) | 253 | self.add_geom(geom,uri,points=points,type=type,owner=owner,object=object,name=name) |
|---|
| 231 | | 254 | |
|---|
| 232 | def add_or_update_point(self,uri,points=[],x=None,y=None,type=None,owner=None,object=None,name=None): | 255 | def add_or_update_point(self,uri,points=[],x=None,y=None,type=None,owner=None,object=None,name=None): |
|---|
| 233 | if x is not None: | 256 | if x is not None: |
|---|
| 234 | points = [(x,y)] | 257 | points = [(x,y)] |
|---|
| 235 | return self.add_or_update_geom('POINT',uri,points=points,type=type,owner=owner,object=object,name=name) | 258 | return self.add_or_update_geom('POINT',uri,points=points,type=type,owner=owner,object=object,name=name) |
|---|
| 236 | | 259 | |
|---|
| 237 | def add_or_update_line(self,uri,points=[],type=None,owner=None,object=None,name=None): | 260 | def add_or_update_line(self,uri,points=[],type=None,owner=None,object=None,name=None): |
|---|
| 238 | return self.add_or_update_geom('LINESTRING',uri,points=points,type=type,owner=owner,object=object,name=name) | 261 | return self.add_or_update_geom('LINESTRING',uri,points=points,type=type,owner=owner,object=object,name=name) |
|---|
| 239 | | 262 | |
|---|
| 240 | def add_or_update_polygon(self,uri,points=[],type=None,owner=None,object=None,name=None): | 263 | def add_or_update_polygon(self,uri,points=[],type=None,owner=None,object=None,name=None): |
|---|
| 241 | points = self.join_into_shape(points) | 264 | points = self.join_into_shape(points) |
|---|
| 242 | return self.add_or_update_geom('POLYGON',uri,points=points,type=type,owner=owner,object=object,name=name) | 265 | return self.add_or_update_geom('POLYGON',uri,points=points,type=type,owner=owner,object=object,name=name) |
|---|