2019-08-15 10:05:51 +08:00

501 lines
18 KiB
Python

#-----------------------------------------------------
# Author: Yaqiang Wang
# Date: 2015-9-20
# Purpose: MeteoInfoLab layer module
# Note: Jython
#-----------------------------------------------------
from org.meteoinfo.data import TableUtil, XYListDataset
from org.meteoinfo.layer import LayerTypes, VectorLayer, ChartSet
from org.meteoinfo.projection import ProjectionUtil, KnownCoordinateSystems
from org.meteoinfo.shape import PolygonShape, ShapeTypes
from org.meteoinfo.legend import LegendType
from java.util import Date, Calendar
from java.awt import Font
from datetime import datetime
import mipylib.miutil as miutil
import mipylib.numeric as np
import geoutil
class MILayer(object):
'''
Map layer
:param layer: (*MapLayer*) MapLayer object.
:param shapetype: (*ShapeTypes*) Shape type ['point' | 'line' | 'polygon']
'''
def __init__(self, layer=None, shapetype=None):
if layer is None:
if shapetype is None:
print 'shapetype must be specified!'
else:
type = ShapeTypes.Point
if shapetype == 'line':
type = ShapeTypes.Polyline
elif shapetype == 'polygon':
type = ShapeTypes.Polygon
self.layer = VectorLayer(type)
self.shapetype = type
self.proj = KnownCoordinateSystems.geographic.world.WGS1984
else:
self.layer = layer
self.shapetype = layer.getShapeType()
self.proj = layer.getProjInfo()
def __repr__(self):
return self.layer.getLayerInfo()
def isvectorlayer(self):
'''
Check this layer is VectorLayer or not.
:returns: (*boolean*) Is VectorLayer or not.
'''
return self.layer.getLayerType() == LayerTypes.VectorLayer
def get_encoding(self):
'''
Get encoding.
:returns: (*string*) Encoding
'''
return self.layer.getAttributeTable().getEncoding()
def gettable(self):
'''
Get attribute table.
:returns: (*PyTableData') Attribute table.
'''
r = self.layer.getAttributeTable().getTable()
return np.datatable(r)
def cellvalue(self, fieldname, shapeindex):
'''
Get attribute table cell value.
:param fieldname: (*string*) Field name.
:param shapeindex: (*int*) Shape index.
:returns: The value in attribute table identified by field name and shape index.
'''
v = self.layer.getCellValue(fieldname, shapeindex)
if isinstance(v, Date):
cal = Calendar.getInstance()
cal.setTime(v)
year = cal.get(Calendar.YEAR)
month = cal.get(Calendar.MONTH) + 1
day = cal.get(Calendar.DAY_OF_MONTH)
hour = cal.get(Calendar.HOUR_OF_DAY)
minute = cal.get(Calendar.MINUTE)
second = cal.get(Calendar.SECOND)
dt = datetime(year, month, day, hour, minute, second)
return dt
else:
return v
def setcellvalue(self, fieldname, shapeindex, value):
'''
Set cell value in attribute table.
:param fieldname: (*string*) Field name.
:param shapeindex: (*int*) Shape index.
:param value: (*object*) Cell value to be asigned.
'''
self.layer.editCellValue(fieldname, shapeindex, value)
def shapes(self):
'''
Get shapes.
'''
return self.layer.getShapes()
def shapenum(self):
'''
Get shape number
'''
return self.layer.getShapeNum()
def legend(self):
'''
Get legend scheme.
'''
return self.layer.getLegendScheme()
def setlegend(self, legend):
'''
Set legend scheme.
:param legend: (*LegendScheme*) Legend scheme.
'''
self.layer.setLegendScheme(legend)
def update_legend(self, ltype, fieldname):
'''
Update legend scheme.
:param ltype: (*string*) Legend type [single | unique | graduate].
:param fieldname: (*string*) Field name.
'''
if ltype == 'single':
ltype = LegendType.SingleSymbol
elif ltype == 'unique':
ltype = LegendType.UniqueValue
elif ltyp == 'graduate':
ltype = LegendType.GraduatedColor
else:
raise ValueError(ltype)
self.layer.updateLegendScheme(ltype, fieldname)
return self.layer.getLegendScheme()
def addfield(self, fieldname, dtype, values=None):
'''
Add a field into the attribute table.
:param fieldname: (*string*) Field name.
:param dtype: (*string*) Field data type [string | int | float | double].
:param values: (*array_like*) Field values.
'''
dt = TableUtil.toDataTypes(dtype)
self.layer.editAddField(fieldname, dt)
if not values is None:
n = self.shapenum()
for i in range(n):
if i < len(values):
self.layer.editCellValue(fieldname, i, values[i])
def delfield(self, fieldname):
'''
Delete a field from the attribute table.
:param fieldname: (*string*) Filed name.
'''
self.layer.editRemoveField(fieldname)
def renamefield(self, fieldname, newfieldname):
'''
Rename the field.
:param fieldname: (*string*) The old field name.
:param newfieldname: (*string*) The new field name.
'''
self.layer.editRenameField(fieldname, newfieldname)
def addshape(self, x, y, fields=None, z=None, m=None):
'''
Add a shape.
:param x: (*array_like*) X coordinates of the shape points.
:param y: (*array_like*) Y coordinates of the shape points.
:param fields: (*array_like*) Field values of the shape.
:param z: (*array_like*) Optional, Z coordinates of the shape points.
:param m: (*array_like*) Optional, M coordinates of the shape points.
'''
type = 'point'
if self.shapetype == ShapeTypes.Polyline:
type = 'line'
elif self.shapetype == ShapeTypes.Polygon:
type = 'polygon'
shapes = geoutil.makeshapes(x, y, type, z, m)
if len(shapes) == 1:
self.layer.editAddShape(shapes[0], fields)
else:
for shape, field in zip(shapes, fields):
self.layer.editAddShape(shape, field)
def addlabels(self, fieldname, **kwargs):
'''
Add labels
:param fieldname: (*string*) Field name
:param fontname: (*string*) Font name. Default is ``Arial``.
:param fontsize: (*string*) Font size. Default is ``14``.
:param bold: (*boolean*) Font bold or not. Default is ``False``.
:param color: (*color*) Label color. Default is ``None`` with black color.
:param xoffset: (*int*) X coordinate offset. Default is ``0``.
:param yoffset: (*int*) Y coordinate offset. Default is ``0``.
:param avoidcoll: (*boolean*) Avoid labels collision or not. Default is ``True``.
:param decimals: (*int*) Number of decimals of labels.
'''
labelset = self.layer.getLabelSet()
labelset.setFieldName(fieldname)
fontname = kwargs.pop('fontname', 'Arial')
fontsize = kwargs.pop('fontsize', 14)
bold = kwargs.pop('bold', False)
if bold:
font = Font(fontname, Font.BOLD, fontsize)
else:
font = Font(fontname, Font.PLAIN, fontsize)
labelset.setLabelFont(font)
color = kwargs.pop('color', None)
if not color is None:
color = miutil.getcolor(color)
labelset.setLabelColor(color)
xoffset = kwargs.pop('xoffset', 0)
labelset.setXOffset(xoffset)
yoffset = kwargs.pop('yoffset', 0)
labelset.setYOffset(yoffset)
avoidcoll = kwargs.pop('avoidcoll', True)
labelset.setAvoidCollision(avoidcoll)
decimals = kwargs.pop('decimals', None)
if not decimals is None:
labelset.setAutoDecimal(False)
labelset.setDecimalDigits(decimals)
self.layer.addLabels()
def getlabel(self, text):
'''
Get a label.
:param text: (*string*) The label text.
'''
return self.layer.getLabel(text)
def movelabel(self, label, x=0, y=0):
'''
Move a label.
:param label: (*string*) The label text.
:param x: (*float*) X shift for moving in pixel unit.
:param y: (*float*) Y shift for moving in pixel unit.
'''
self.layer.moveLabel(label, x, y)
def add_charts(self, fieldnames, legend=None, **kwargs):
'''
Add charts
:param fieldnames: (*list of string*) Field name list.
:param legend: (*LegendScheme*) Chart legend.
:param charttype: (*string*) Chart type [bar | pie]. Default is ``bar``.
:param minsize: (*int*) Minimum chart size. Default is ``0``.
:param maxsize: (*int*) Maximum chart size. Default is ``50``.
:param barwidth: (*int*) Bar width. Only valid for bar chart. Default is ``8``.
:param xoffset: (*int*) X coordinate offset. Default is ``0``.
:param yoffset: (*int*) Y coordinate offset. Default is ``0``.
:param avoidcoll: (*boolean*) Avoid labels collision or not. Default is ``True``.
:param align: (*string*) Chart align type [center | left | right | none], Default is ``center``.
:param view3d: (*boolean*) Draw chart as 3D or not. Default is ``False``.
:param thickness: (*int*) 3D chart thickness. Default is ``5``.
:param drawlabel: (*boolean*) Draw label or not. Default is ``False``.
:param fontname: (*string*) Label font name.
:param fontsize: (*int*) Label font size.
:param bold: (*boolean*) Font bold or not. Default is ``False``.
:param labelcolor: (*color*) Label color.
:param decimals: (*int*) Number of decimals of labels.
'''
charttype = kwargs.pop('charttype', None)
minsize = kwargs.pop('minsize', None)
maxsize = kwargs.pop('maxsize', None)
barwidth = kwargs.pop('barwidth', None)
xoffset = kwargs.pop('xoffset', None)
yoffset = kwargs.pop('yoffset', None)
avoidcoll = kwargs.pop('avoidcoll', None)
align = kwargs.pop('align', None)
view3d = kwargs.pop('view3d', None)
thickness = kwargs.pop('thickness', None)
drawlabel = kwargs.pop('drawlabel', None)
fontname = kwargs.pop('fontname', 'Arial')
fontsize = kwargs.pop('fontsize', 12)
bold = kwargs.pop('bold', False)
if bold:
font = Font(fontname, Font.BOLD, fontsize)
else:
font = Font(fontname, Font.PLAIN, fontsize)
labelcolor = kwargs.pop('labelcolor', None)
decimals = kwargs.pop('decimals', None)
chartset = self.layer.getChartSet()
chartset.setFieldNames(fieldnames)
chartset.setLegendScheme(legend)
if not charttype is None:
chartset.setChartType(charttype)
if not minsize is None:
chartset.setMinSize(minsize)
if not maxsize is None:
chartset.setMaxSize(maxsize)
if not barwidth is None:
chartset.setBarWidth(barwidth)
if not xoffset is None:
chartset.setXShift(xoffset)
if not yoffset is None:
chartset.setYShift(yoffset)
if not avoidcoll is None:
chartset.setAvoidCollision(avoidcoll)
if not align is None:
chartset.setAlignType(align)
if not view3d is None:
chartset.setView3D(view3d)
if not thickness is None:
chartset.setThickness(thickness)
if not drawlabel is None:
chartset.setDrawLabel(drawlabel)
chartset.setLabelFont(font)
if not labelcolor is None:
chartset.setLabelColor(miutil.getcolor(labelcolor))
if not decimals is None:
chartset.setDecimalDigits(decimals)
self.layer.updateChartSet()
self.layer.addCharts()
return chartset
def get_chartlegend(self):
'''
Get legend of the chart graphics.
'''
return self.layer.getChartSet().getLegendScheme()
def get_chart(self, index):
'''
Get a chart graphic.
:param index: (*int*) Chart index.
:returns: Chart graphic
'''
return self.layer.getChartPoints()[index]
def move_chart(self, index, x=0, y=0):
'''
Move a chart graphic.
:param index: (*int*) Chart index.
:param x: (*float*) X shift for moving.
:param y: (*float*) Y shift for moving.
'''
s = self.layer.getChartPoints()[index].getShape()
p = s.getPoint()
p.X = p.X + x
p.Y = p.Y + y
s.setPoint(p)
def set_avoidcoll(self, avoidcoll):
'''
Set if avoid collision or not. Only valid for VectorLayer with Point shapes.
:param avoidcoll: (*boolean*) Avoid collision or not.
'''
self.layer.setAvoidCollision(avoidcoll)
def project(self, toproj):
'''
Project to another projection.
:param toproj: (*ProjectionInfo*) The projection to be projected.
'''
ProjectionUtil.projectLayer(self.layer, toproj)
def buffer(self, dist=0, merge=False):
'''
Get the buffer layer.
:param dist: (*float*) Buffer value.
:param merge: (*boolean*) Merge the buffered shapes or not.
:returns: (*MILayer*) Buffered layer.
'''
r = self.layer.buffer(dist, False, merge)
return MILayer(r)
def clip(self, clipobj):
'''
Clip this layer by polygon or another polygon layer.
:param clipobj: (*PolygonShape or MILayer*) Clip object.
:returns: (*MILayer*) Clipped layer.
'''
if isinstance(clipobj, PolygonShape):
clipobj = [clipobj]
elif isinstance(clipobj, MILayer):
clipobj = clipobj.layer
r = self.layer.clip(clipobj)
return MILayer(r)
def select(self, expression, seltype='new'):
'''
Select shapes by SQL expression.
:param expression: (*string*) SQL expression.
:param seltype: (*string*) Selection type ['new' | 'add_to_current' |
'remove_from_current' | 'select_from_current']
:returns: (*list of Shape*) Selected shape list.
'''
self.layer.sqlSelect(expression, seltype)
return self.layer.getSelectedShapes()
def clear_selection(self):
'''
Clear shape selection.
'''
self.layer.clearSelectedShapes()
def clone(self):
'''
Clone self.
'''
return MILayer(self.layer.clone())
def save(self, fn=None, encoding=None):
"""
Save layer as shape file.
:param fn: (*string*) Shape file name (.shp).
:param encoding: (*string*) Encoding.
"""
if fn is None:
fn = self.layer.getFileName()
if fn.strip() == '':
print 'File name is needed to save the layer!'
raise IOError
else:
if encoding is None:
self.layer.saveFile(fn)
else:
self.layer.saveFile(fn, encoding)
def savekml(self, fn):
"""
Save layer as KML file.
:param fn: (*string*) KML file name.
"""
self.layer.saveAsKMLFile(fn)
class MIXYListData():
def __init__(self, data=None):
if data is None:
self.data = XYListDataset()
else:
self.data = data
def __getitem__(self, indices):
if not isinstance(indices, tuple):
inds = []
inds.append(indices)
indices = inds
if isinstance(indices[0], int):
if isinstance(indices[1], int):
x = self.data.getX(indices[0], indices[1])
y = self.data.getY(indices[0], indices[1])
return x, y
else:
return self.data.getXValues(indices[0]), self.data.getXValues(indices[0])
def size(self, series=None):
if series is None:
return self.data.getSeriesCount()
else:
return self.data.getItemCount(series)
def addseries(self, xdata, ydata, key=None):
if key is None:
key = 'Series_' + str(self.size())
if isinstance(xdata, list):
self.data.addSeries(key, xdata, ydata)
else:
self.data.addSeries(key, xdata.asarray(), ydata.asarray())