123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- # -*- coding: utf-8 -*-
- """
- Fast and exit-safe interface to PyGRASS Raster and Vector layer
- using multiprocessing
- (C) 2015 by the GRASS Development Team
- This program is free software under the GNU General Public
- License (>=v2). Read the file COPYING that comes with GRASS
- for details.
- :authors: Soeren Gebbert
- """
- import time
- import threading
- import sys
- from multiprocessing import Process, Lock, Pipe
- from ctypes import *
- from grass.exceptions import FatalError
- from grass.pygrass.vector import *
- from grass.pygrass.raster import *
- import grass.lib.gis as libgis
- from base import RPCServerBase
- from grass.pygrass.gis.region import Region
- import grass.pygrass.utils as utils
- import logging
- ###############################################################################
- ###############################################################################
- class RPCDefs(object):
- # Function identifier and index
- STOP = 0
- GET_VECTOR_TABLE_AS_DICT = 1
- GET_VECTOR_FEATURES_AS_WKB = 2
- GET_RASTER_IMAGE_AS_NP = 3
- G_FATAL_ERROR = 14
- def _get_raster_image_as_np(lock, conn, data):
- """Convert a raster map into an image and return
- a numpy array with RGB or Gray values.
- :param lock: A multiprocessing.Lock instance
- :param conn: A multiprocessing.Pipe instance used to send True or False
- :param data: The list of data entries [function_id, raster_name, extent, color]
- """
- array = None
- try:
- name = data[1]
- mapset = data[2]
- extent = data[3]
- color = data[4]
- mapset = utils.get_mapset_raster(name, mapset)
- if not mapset:
- raise ValueError("Unable to find raster map <%s>"%(name))
- rast = RasterRow(name, mapset)
- if rast.exist():
- reg = Region()
- reg.from_rast(name)
- if extent is not None:
- if "north" in extent:
- reg.north = extent["north"]
- if "south" in extent:
- reg.south = extent["south"]
- if "east" in extent:
- reg.east = extent["east"]
- if "west" in extent:
- reg.west = extent["west"]
- if "rows" in extent:
- reg.rows = extent["rows"]
- if "cols" in extent:
- reg.cols = extent["cols"]
- reg.adjust()
- array = raster2numpy_img(name, reg, color)
- except:
- raise
- finally:
- conn.send(array)
- def _get_vector_table_as_dict(lock, conn, data):
- """Get the table of a vector map layer as dictionary
- :param lock: A multiprocessing.Lock instance
- :param conn: A multiprocessing.Pipe instance used to send True or False
- :param data: The list of data entries [function_id, name, mapset, where]
- """
- ret = None
- try:
- name = data[1]
- mapset = data[2]
- where = data[3]
- mapset = utils.get_mapset_vector(name, mapset)
- if not mapset:
- raise ValueError("Unable to find vector map <%s>"%(name))
- layer = VectorTopo(name, mapset)
- if layer.exist() is True:
- layer.open("r")
- columns = None
- table = None
- if layer.table is not None:
- columns = layer.table.columns
- table = layer.table_to_dict(where=where)
- layer.close()
- ret = {}
- ret["table"] = table
- ret["columns"] = columns
- except:
- raise
- finally:
- conn.send(ret)
- def _get_vector_features_as_wkb_list(lock, conn, data):
- """Return vector layer features as wkb list
- supported feature types:
- point, centroid, line, boundary, area
- :param lock: A multiprocessing.Lock instance
- :param conn: A multiprocessing.Pipe instance used to send True or False
- :param data: The list of data entries [function_id,name,mapset,extent,
- feature_type, field]
- """
- wkb_list = None
- try:
- name = data[1]
- mapset = data[2]
- extent = data[3]
- feature_type = data[4]
- field = data[5]
- bbox = None
- mapset = utils.get_mapset_vector(name, mapset)
- if not mapset:
- raise ValueError("Unable to find vector map <%s>"%(name))
- layer = VectorTopo(name, mapset)
- if layer.exist() is True:
- if extent is not None:
- bbox = basic.Bbox(north=extent["north"],
- south=extent["south"],
- east=extent["east"],
- west=extent["west"])
- layer.open("r")
- if feature_type.lower() == "area":
- wkb_list = layer.areas_to_wkb_list(bbox=bbox, field=field)
- else:
- wkb_list = layer.features_to_wkb_list(bbox=bbox,
- feature_type=feature_type,
- field=field)
- layer.close()
- except:
- raise
- finally:
- conn.send(wkb_list)
- ###############################################################################
- def _fatal_error(lock, conn, data):
- """Calls G_fatal_error()"""
- libgis.G_fatal_error("Fatal Error in C library server")
- ###############################################################################
- def _stop(lock, conn, data):
- conn.close()
- lock.release()
- sys.exit()
- ###############################################################################
- def data_provider_server(lock, conn):
- """The PyGRASS data provider server designed to be a target for
- multiprocessing.Process
- :param lock: A multiprocessing.Lock
- :param conn: A multiprocessing.Pipe
- """
- def error_handler(data):
- """This function will be called in case of a fatal error in libgis"""
- #sys.stderr.write("Error handler was called\n")
- # We send an exeption that will be handled in
- # the parent process, then close the pipe
- # and release any possible lock
- conn.send(FatalError())
- conn.close()
- lock.release()
- CALLBACK = CFUNCTYPE(c_void_p, c_void_p)
- CALLBACK.restype = c_void_p
- CALLBACK.argtypes = c_void_p
- cerror_handler = CALLBACK(error_handler)
- libgis.G_add_error_handler(cerror_handler, None)
- # Crerate the function array
- functions = [0]*15
- functions[RPCDefs.GET_VECTOR_TABLE_AS_DICT] = _get_vector_table_as_dict
- functions[RPCDefs.GET_VECTOR_FEATURES_AS_WKB] = _get_vector_features_as_wkb_list
- functions[RPCDefs.GET_RASTER_IMAGE_AS_NP] = _get_raster_image_as_np
- functions[RPCDefs.STOP] = _stop
- functions[RPCDefs.G_FATAL_ERROR] = _fatal_error
- while True:
- # Avoid busy waiting
- conn.poll(None)
- data = conn.recv()
- lock.acquire()
- functions[data[0]](lock, conn, data)
- lock.release()
- test_vector_name="data_provider_vector_map"
- test_raster_name="data_provider_raster_map"
- class DataProvider(RPCServerBase):
- """Fast and exit-safe interface to PyGRASS data delivery functions
- """
- def __init__(self):
- RPCServerBase.__init__(self)
- def start_server(self):
- """This function must be re-implemented in the subclasses
- """
- self.client_conn, self.server_conn = Pipe(True)
- self.lock = Lock()
- self.server = Process(target=data_provider_server, args=(self.lock,
- self.server_conn))
- self.server.daemon = True
- self.server.start()
- def get_raster_image_as_np(self, name, mapset=None, extent=None, color="RGB"):
- """Return the attribute table of a vector map as dictionary.
- See documentation of: pygrass.raster.raster2numpy_img
- Usage:
- .. code-block:: python
- >>> from grass.pygrass.rpc import DataProvider
- >>> provider = DataProvider()
- >>> ret = provider.get_raster_image_as_np(name=test_raster_name)
- >>> len(ret)
- 64
- >>> extent = {"north":30, "south":10, "east":30, "west":10,
- ... "rows":2, "cols":2}
- >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
- ... extent=extent)
- >>> len(ret)
- 16
- >>> extent = {"rows":3, "cols":1}
- >>> ret = provider.get_raster_image_as_np(name=test_raster_name,
- ... extent=extent)
- >>> len(ret)
- 12
- >>> provider.stop()
- ..
- """
- self.check_server()
- self.client_conn.send([RPCDefs.GET_RASTER_IMAGE_AS_NP,
- name, mapset, extent, color])
- return self.safe_receive("get_raster_image_as_np")
- def get_vector_table_as_dict(self, name, mapset=None, where=None):
- """Return the attribute table of a vector map as dictionary.
- See documentation of: pygrass.vector.VectorTopo::table_to_dict
- Usage:
- .. code-block:: python
- >>> from grass.pygrass.rpc import DataProvider
- >>> provider = DataProvider()
- >>> ret = provider.get_vector_table_as_dict(name=test_vector_name)
- >>> ret["table"]
- {1: [1, u'point', 1.0], 2: [2, u'line', 2.0], 3: [3, u'centroid', 3.0]}
- >>> ret["columns"]
- Columns([(u'cat', u'INTEGER'), (u'name', u'varchar(50)'), (u'value', u'double precision')])
- >>> ret = provider.get_vector_table_as_dict(name=test_vector_name,
- ... where="value > 1")
- >>> ret["table"]
- {2: [2, u'line', 2.0], 3: [3, u'centroid', 3.0]}
- >>> ret["columns"]
- Columns([(u'cat', u'INTEGER'), (u'name', u'varchar(50)'), (u'value', u'double precision')])
- >>> provider.get_vector_table_as_dict(name="no_map",
- ... where="value > 1")
- >>> provider.stop()
- ..
- """
- self.check_server()
- self.client_conn.send([RPCDefs.GET_VECTOR_TABLE_AS_DICT,
- name, mapset, where])
- return self.safe_receive("get_vector_table_as_dict")
- def get_vector_features_as_wkb_list(self, name, mapset=None, extent=None,
- feature_type="point", field=1):
- """Return the features of a vector map as wkb list.
- :param extent: A dictionary of {"north":double, "south":double,
- "east":double, "west":double}
- :param feature_type: point, centroid, line, boundary or area
- See documentation: pygrass.vector.VectorTopo::features_to_wkb_list
- pygrass.vector.VectorTopo::areas_to_wkb_list
- Usage:
- .. code-block:: python
- >>> from grass.pygrass.rpc import DataProvider
- >>> provider = DataProvider()
- >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
- ... extent=None,
- ... feature_type="point")
- >>> for entry in wkb:
- ... f_id, cat, string = entry
- ... print(f_id, cat, len(string))
- 1 1 21
- 2 1 21
- 3 1 21
- >>> extent = {"north":6.6, "south":5.5, "east":14.5, "west":13.5}
- >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
- ... extent=extent,
- ... feature_type="point")
- >>> for entry in wkb:
- ... f_id, cat, string = entry
- ... print(f_id, cat, len(string))
- 3 1 21
- >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
- ... extent=None,
- ... feature_type="line")
- >>> for entry in wkb:
- ... f_id, cat, string = entry
- ... print(f_id, cat, len(string))
- 4 2 57
- 5 2 57
- 6 2 57
- >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
- ... extent=None,
- ... feature_type="centroid")
- >>> for entry in wkb:
- ... f_id, cat, string = entry
- ... print(f_id, cat, len(string))
- 19 3 21
- 18 3 21
- 20 3 21
- 21 3 21
- >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
- ... extent=None,
- ... feature_type="area")
- >>> for entry in wkb:
- ... f_id, cat, string = entry
- ... print(f_id, cat, len(string))
- 1 3 225
- 2 3 141
- 3 3 93
- 4 3 141
- >>> wkb = provider.get_vector_features_as_wkb_list(name=test_vector_name,
- ... extent=None,
- ... feature_type="boundary")
- >>> for entry in wkb:
- ... f_id, cat, string = entry
- ... print(f_id, cat, len(string))
- 10 None 41
- 7 None 41
- 8 None 41
- 9 None 41
- 11 None 89
- 12 None 41
- 14 None 41
- 13 None 41
- 17 None 41
- 15 None 41
- 16 None 41
- >>> provider.stop()
- ..
- """
- self.check_server()
- self.client_conn.send([RPCDefs.GET_VECTOR_FEATURES_AS_WKB,
- name, mapset, extent, feature_type, field])
- return self.safe_receive("get_vector_features_as_wkb_list")
- if __name__ == "__main__":
- import doctest
- from grass.pygrass import utils
- from grass.pygrass.modules import Module
- Module("g.region", n=40, s=0, e=40, w=0, res=10)
- Module("r.mapcalc", expression="%s = row() + (10 * col())"%(test_raster_name),
- overwrite=True)
- utils.create_test_vector_map(test_vector_name)
- doctest.testmod()
- """Remove the generated maps, if exist"""
- mset = utils.get_mapset_raster(test_raster_name, mapset='')
- if mset:
- Module("g.remove", flags='f', type='raster', name=test_raster_name)
- mset = utils.get_mapset_vector(test_vector_name, mapset='')
- if mset:
- Module("g.remove", flags='f', type='vector', name=test_vector_name)
|