1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
|
# -*- coding: utf-8 -*-
"""
Created on Thu Jun 28 17:44:14 2012
@author: pietro
"""
import ctypes
from operator import itemgetter
import grass.lib.raster as libraster
from grass.pygrass.errors import GrassError
from grass.pygrass.raster.raster_type import TYPE as RTYPE
class Category(list):
"""
I would like to add the following functions:
Getting the umber of cats:
Rast_number_of_cats() <- Important for ith access
Getting and setting the title:
Rast_get_cats_title()
Rast_set_cats_title()
Do not use these functions for category access:
Rast_get_cat()
and the specialized types for CELL, FCELL and DCELL.
Since these functions are working on hidden static buffer.
Use the ith-get methods:
Rast_get_ith_c_cat()
Rast_get_ith_f_cat()
Rast_get_ith_d_cat()
This can be implemented using an iterator too. So that the category object
provides the [] access operator to the categories, returning a tuple
(label, min, max).
Using this, the category object must be aware of its raster map type.
Set categories using:
Rast_set_c_cat()
Rast_set_f_cat()
Rast_set_d_cat()
Misc:
Rast_sort_cats()
Rast_copy_cats() <- This should be wrapped so that categories from an
existing Python category class are copied.
"""
def __init__(self, name, mapset='', mtype='CELL', *args, **kargs):
self.name = name
self.mapset = mapset
self.c_cats = libraster.Categories()
libraster.Rast_init_cats("", ctypes.byref(self.c_cats))
self._mtype = mtype
self._gtype = None if mtype is None else RTYPE[mtype]['grass type']
super(Category, self).__init__(*args, **kargs)
def _get_mtype(self):
return self._mtype
def _set_mtype(self, mtype):
if mtype.upper() not in ('CELL', 'FCELL', 'DCELL'):
#fatal(_("Raser type: {0} not supported".format(mtype) ) )
raise ValueError(_("Raster type: {0} not supported".format(mtype)))
self._mtype = mtype
self._gtype = RTYPE[self.mtype]['grass type']
mtype = property(fget=_get_mtype, fset=_set_mtype,
doc="Set or obtain raster data type")
def _get_title(self):
return libraster.Rast_get_cats_title(ctypes.byref(self.c_cats))
def _set_title(self, newtitle):
return libraster.Rast_set_cats_title(newtitle,
ctypes.byref(self.c_cats))
title = property(fget=_get_title, fset=_set_title,
doc="Set or obtain raster title")
def __str__(self):
return self.__repr__()
def __list__(self):
cats = []
for cat in self.__iter__():
cats.append(cat)
return cats
def __dict__(self):
diz = dict()
for cat in self.__iter__():
label, min_cat, max_cat = cat
diz[(min_cat, max_cat)] = label
return diz
def __repr__(self):
cats = []
for cat in self.__iter__():
cats.append(repr(cat))
return "[{0}]".format(',\n '.join(cats))
def _chk_index(self, index):
if type(index) == str:
try:
index = self.labels().index(index)
except ValueError:
raise KeyError(index)
return index
def _chk_value(self, value):
if type(value) == tuple:
length = len(value)
if length == 2:
label, min_cat = value
value = (label, min_cat, None)
elif length < 2 or length > 3:
raise TypeError('Tuple with a length that is not supported.')
else:
raise TypeError('Only Tuple are supported.')
return value
def __getitem__(self, index):
return super(Category, self).__getitem__(self._chk_index(index))
def __setitem__(self, index, value):
return super(Category, self).__setitem__(self._chk_index(index),
self._chk_value(value))
def _get_c_cat(self, index):
"""Returns i-th description and i-th data range from the list of
category descriptions with corresponding data ranges. end points of
data interval.
Rast_get_ith_cat(const struct Categories * pcats,
int i,
void * rast1,
void * rast2,
RASTER_MAP_TYPE data_type
)
"""
min_cat = ctypes.pointer(RTYPE[self.mtype]['grass def']())
max_cat = ctypes.pointer(RTYPE[self.mtype]['grass def']())
lab = libraster.Rast_get_ith_cat(ctypes.byref(self.c_cats),
index,
ctypes.cast(min_cat, ctypes.c_void_p),
ctypes.cast(max_cat, ctypes.c_void_p),
self._gtype)
# Manage C function Errors
if lab == '':
raise GrassError(_("Error executing: Rast_get_ith_cat"))
if max_cat.contents.value == min_cat.contents.value:
max_cat = None
else:
max_cat = max_cat.contents.value
return lab, min_cat.contents.value, max_cat
def _set_c_cat(self, label, min_cat, max_cat=None):
"""Adds the label for range min through max in category structure cats.
int Rast_set_cat(const void * rast1,
const void * rast2,
const char * label,
struct Categories * pcats,
RASTER_MAP_TYPE data_type
)
"""
max_cat = min_cat if max_cat is None else max_cat
min_cat = ctypes.pointer(RTYPE[self.mtype]['grass def'](min_cat))
max_cat = ctypes.pointer(RTYPE[self.mtype]['grass def'](max_cat))
err = libraster.Rast_set_cat(ctypes.cast(min_cat, ctypes.c_void_p),
ctypes.cast(max_cat, ctypes.c_void_p),
label,
ctypes.byref(self.c_cats), self._gtype)
# Manage C function Errors
if err == 1:
return None
elif err == 0:
raise GrassError(_("Null value detected"))
elif err == -1:
raise GrassError(_("Error executing: Rast_set_cat"))
def __del__(self):
libraster.Rast_free_cats(ctypes.byref(self.c_cats))
def get_cat(self, index):
return self.__getitem__(index)
def set_cat(self, index, value):
if index is None:
self.append(value)
elif index < self.__len__():
self.__setitem__(index, value)
else:
raise TypeError("Index outside range.")
def reset(self):
for i in range(len(self) - 1, -1, -1):
del(self[i])
libraster.Rast_init_cats("", ctypes.byref(self.c_cats))
def _read_cats(self):
"""Copy from the C struct to the list"""
for i in range(self.c_cats.ncats):
self.append(self._get_c_cat(i))
def _write_cats(self):
"""Copy from the list data to the C struct"""
# reset only the C struct
libraster.Rast_init_cats("", ctypes.byref(self.c_cats))
# write to the c struct
for cat in self.__iter__():
label, min_cat, max_cat = cat
if max_cat is None:
max_cat = min_cat
self._set_c_cat(label, min_cat, max_cat)
def read(self):
"""Read categories from a raster map
The category file for raster map name in mapset is read into the
cats structure. If there is an error reading the category file,
a diagnostic message is printed.
int Rast_read_cats(const char * name,
const char * mapset,
struct Categories * pcats
)
"""
self.reset()
err = libraster.Rast_read_cats(self.name, self.mapset,
ctypes.byref(self.c_cats))
if err == -1:
raise GrassError("Can not read the categories.")
# copy from C struct to list
self._read_cats()
def write(self):
"""Writes the category file for the raster map name in the current
mapset from the cats structure.
void Rast_write_cats(const char * name,
struct Categories * cats
)
"""
# copy from list to C struct
self._write_cats()
# write to the map
libraster.Rast_write_cats(self.name, ctypes.byref(self.c_cats))
def copy(self, category):
"""Copy from another Category class
:param category: Category class to be copied
:type category: Category object
"""
libraster.Rast_copy_cats(ctypes.byref(self.c_cats), # to
ctypes.byref(category.c_cats)) # from
self._read_cats()
def ncats(self):
return self.__len__()
def set_cats_fmt(self, fmt, m1, a1, m2, a2):
"""Not implemented yet.
void Rast_set_cats_fmt()
"""
#TODO: add
pass
def read_rules(self, filename, sep=':'):
"""Copy categories from a rules file, default separetor is ':', the
columns must be: min and/or max and label. ::
1:forest
2:road
3:urban
0.:0.5:forest
0.5:1.0:road
1.0:1.5:urban
:param str filename: the name of file with categories rules
:param str sep: the separator used to divide values and category
"""
self.reset()
with open(filename, 'r') as f:
for row in f.readlines():
cat = row.strip().split(sep)
if len(cat) == 2:
label, min_cat = cat
max_cat = None
elif len(cat) == 3:
label, min_cat, max_cat = cat
else:
raise TypeError("Row length is greater than 3")
#import pdb; pdb.set_trace()
self.append((label, min_cat, max_cat))
def write_rules(self, filename, sep=':'):
"""Copy categories from a rules file, default separetor is ':', the
columns must be: min and/or max and label. ::
1:forest
2:road
3:urban
0.:0.5:forest
0.5:1.0:road
1.0:1.5:urban
:param str filename: the name of file with categories rules
:param str sep: the separator used to divide values and category
"""
with open(filename, 'w') as f:
cats = []
for cat in self.__iter__():
if cat[-1] is None:
cat = cat[:-1]
cats.append(sep.join([str(i) for i in cat]))
f.write('\n'.join(cats))
def sort(self):
libraster.Rast_sort_cats(ctypes.byref(self.c_cats))
def labels(self):
return list(map(itemgetter(0), self))
|