diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/geo/MapPlot.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/geo/MapPlot.java index 284bc293..f50bb489 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/geo/MapPlot.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/geo/MapPlot.java @@ -18,6 +18,8 @@ import org.meteoinfo.data.mapdata.webmap.GeoPosition; import org.meteoinfo.data.mapdata.webmap.GeoUtil; import org.meteoinfo.data.mapdata.webmap.IWebMapPanel; import org.meteoinfo.data.mapdata.webmap.TileLoadListener; +import org.meteoinfo.geometry.graphic.Transform; +import org.meteoinfo.projection.*; import org.meteoinfo.render.java2d.Draw; import org.meteoinfo.chart.graphic.GeoGraphicCollection; import org.meteoinfo.geometry.graphic.Graphic; @@ -25,10 +27,6 @@ import org.meteoinfo.geometry.graphic.GraphicCollection; import org.meteoinfo.chart.graphic.GraphicProjectionUtil; import org.meteoinfo.geometry.legend.*; import org.meteoinfo.geometry.shape.*; -import org.meteoinfo.projection.KnownCoordinateSystems; -import org.meteoinfo.projection.ProjectionInfo; -import org.meteoinfo.projection.ProjectionUtil; -import org.meteoinfo.projection.Reproject; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.SAXException; @@ -646,6 +644,41 @@ public class MapPlot extends Plot2D implements IWebMapPanel { graphic.draw(g, area, this.tileLoadListener); } + /** + * Add a graphic + * + * @param g Graphic + */ + @Override + public Graphic addGraphic(Graphic graphic) { + Transform transform = graphic.getTransform(); + if (transform != null && transform.isValid()) { + GeoTransform geoTransform = (GeoTransform) transform; + graphic = GraphicProjectionUtil.projectClipGraphic(graphic, geoTransform.getSourceProj(), + geoTransform.getTargetProj()); + } + + return super.addGraphic(graphic); + } + + /** + * Add a graphic by index + * + * @param idx Index + * @param g Graphic + */ + @Override + public Graphic addGraphic(int idx, Graphic graphic) { + Transform transform = graphic.getTransform(); + if (transform != null && transform.isValid()) { + GeoTransform geoTransform = (GeoTransform) transform; + GraphicProjectionUtil.projectClipGraphic(graphic, geoTransform.getSourceProj(), + geoTransform.getTargetProj()); + } + + return super.addGraphic(idx, graphic); + } + /** * Add a graphic * @@ -656,12 +689,12 @@ public class MapPlot extends Plot2D implements IWebMapPanel { public Graphic addGraphic(Graphic graphic, ProjectionInfo proj) { ProjectionInfo toProj = this.getProjInfo(); if (proj.equals(toProj)) { - this.addGraphic(graphic); + super.addGraphic(graphic); return graphic; } else { Graphic nGraphic = GraphicProjectionUtil.projectClipGraphic(graphic, proj, toProj); if (nGraphic != null) { - this.addGraphic(nGraphic); + super.addGraphic(nGraphic); } return nGraphic; } diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicProjectionUtil.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicProjectionUtil.java index 027f8a4c..cf76181b 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicProjectionUtil.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicProjectionUtil.java @@ -7,10 +7,7 @@ import org.meteoinfo.chart.graphic.GeoGraphicCollection; import org.meteoinfo.geometry.geoprocess.GeoComputation; import org.meteoinfo.geometry.graphic.Graphic; import org.meteoinfo.geometry.shape.Shape; -import org.meteoinfo.projection.ProjectionInfo; -import org.meteoinfo.projection.ProjectionNames; -import org.meteoinfo.projection.ProjectionUtil; -import org.meteoinfo.projection.Reproject; +import org.meteoinfo.projection.*; import org.meteoinfo.table.DataColumn; import org.meteoinfo.table.DataRow; import org.meteoinfo.table.DataTable; @@ -21,6 +18,7 @@ import java.util.logging.Level; import java.util.logging.Logger; public class GraphicProjectionUtil extends ProjectionUtil { + /** * Project graphic * diff --git a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Graphic.java b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Graphic.java index 8a89f9ad..ddedf371 100644 --- a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Graphic.java +++ b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Graphic.java @@ -39,6 +39,7 @@ package org.meteoinfo.geometry.graphic; protected ColorBreak legend; protected GeneralPath clipPath; protected Graphic clipGraphic; + protected Transform transform; private ResizeAbility _resizeAbility = ResizeAbility.RESIZE_ALL; protected int handle; // @@ -101,6 +102,22 @@ package org.meteoinfo.geometry.graphic; updateResizeAbility(); } + /** + * Get transform + * @return Transform + */ + public Transform getTransform() { + return this.transform; + } + + /** + * Set transform + * @param value Transform + */ + public void setTransform(Transform value) { + this.transform = value; + } + /** * Get clip path * @return Clip path diff --git a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/GraphicCollection.java b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/GraphicCollection.java index ae7d0458..ac806700 100644 --- a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/GraphicCollection.java +++ b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/GraphicCollection.java @@ -100,6 +100,7 @@ public class GraphicCollection extends Graphic implements Iterator { */ public void setGraphics(List value) { this.graphics = value; + this.updateExtent(); } /** diff --git a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Line2DGraphic.java b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Line2DGraphic.java index 5d355fa5..d7786177 100644 --- a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Line2DGraphic.java +++ b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Line2DGraphic.java @@ -167,10 +167,10 @@ public class Line2DGraphic extends Graphic { } /** - * Get data - * @return Data + * Get color data array + * @return Color data array */ - public Array getData() { + public Array getColorData() { return this.cData; } diff --git a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Point2DGraphicCollection.java b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Point2DGraphicCollection.java index 06f13a84..b1874e06 100644 --- a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Point2DGraphicCollection.java +++ b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Point2DGraphicCollection.java @@ -8,6 +8,7 @@ import org.meteoinfo.geometry.legend.PointBreak; import org.meteoinfo.geometry.shape.PointShape; import org.meteoinfo.geometry.shape.PolylineShape; import org.meteoinfo.geometry.shape.Shape; +import org.meteoinfo.geometry.shape.ShapeTypes; import org.meteoinfo.ndarray.Array; import org.meteoinfo.ndarray.IndexIterator; @@ -47,7 +48,6 @@ public class Point2DGraphicCollection extends GraphicCollection { this(); this.xData = xData; this.yData = yData; - this.legend = pointBreak; this.updateGraphics(pointBreak); } @@ -55,6 +55,20 @@ public class Point2DGraphicCollection extends GraphicCollection { * Constructor * @param xData X data * @param yData Y data + * @param pointBreak Point break + */ + public Point2DGraphicCollection(Array xData, Array yData, List cbs) { + this(); + this.xData = xData; + this.yData = yData; + this.updateGraphics(cbs); + } + + /** + * Constructor + * + * @param xData X data + * @param yData Y data * @param cData Color data * @param ls Legend scheme */ @@ -63,10 +77,18 @@ public class Point2DGraphicCollection extends GraphicCollection { this.xData = xData; this.yData = yData; this.cData = cData; - this.legendScheme = ls; this.updateGraphics(ls); } + /** + * Return has color data array or not + * + * @return Has color data array of not + */ + public boolean hasColorData() { + return this.cData != null; + } + /** * Get x data * @return X data @@ -75,6 +97,15 @@ public class Point2DGraphicCollection extends GraphicCollection { return this.xData; } + /** + * Set x data + * @param xData X data + */ + public void setXData(Array xData) { + this.xData = xData; + updateShape(); + } + /** * Get y data * @return Y data @@ -84,18 +115,36 @@ public class Point2DGraphicCollection extends GraphicCollection { } /** - * Set data - * @return Data + * Set y data + * @param yData Y data */ - public Array getData() { + public void setYData(Array yData) { + this.yData = yData; + updateShape(); + } + + /** + * Get color data + * @return Color data + */ + public Array getColorData() { return this.cData; } + protected void updateShape() { + if (this.hasColorData()) { + updateGraphics(this.legendScheme); + } else { + updateGraphics((PointBreak) this.legend); + } + } + protected void updateGraphics() { updateGraphics((PointBreak) this.legend); } protected void updateGraphics(PointBreak pointBreak) { + this.legend = pointBreak; this.graphics = new ArrayList<>(); List points = new ArrayList<>(); IndexIterator xIter = this.xData.getIndexIterator(); @@ -112,6 +161,35 @@ public class Point2DGraphicCollection extends GraphicCollection { } } + protected void updateGraphics(List cbs) { + this.graphics = new ArrayList<>(); + List points = new ArrayList<>(); + IndexIterator xIter = this.xData.getIndexIterator(); + IndexIterator yIter = this.yData.getIndexIterator(); + double x, y; + if (cbs.size() == this.xData.getSize()) { + int i = 0; + while (xIter.hasNext()) { + x = xIter.getDoubleNext(); + y = yIter.getDoubleNext(); + if (Double.isNaN(x) || Double.isNaN(y)) { + continue; + } + PointShape shape = new PointShape(new PointD(x, y)); + this.add(new Point2DGraphic(shape, (PointBreak) cbs.get(i))); + i += 1; + } + LegendScheme ls = new LegendScheme(); + ls.setLegendBreaks(cbs); + ls.setLegendType(LegendType.UNIQUE_VALUE); + ls.setShapeType(ShapeTypes.POINT); + this.singleLegend = false; + this.legendScheme = ls; + } else { + updateGraphics((PointBreak) cbs.get(0)); + } + } + protected void updateGraphics(LegendScheme ls) { this.graphics = new ArrayList(); PointShape ps; @@ -143,7 +221,7 @@ public class Point2DGraphicCollection extends GraphicCollection { } } } - this.setSingleLegend(false); - this.setLegendScheme(ls); + this.singleLegend = false; + this.legendScheme = ls; } } diff --git a/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Transform.java b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Transform.java new file mode 100644 index 00000000..d76ed486 --- /dev/null +++ b/meteoinfo-geometry/src/main/java/org/meteoinfo/geometry/graphic/Transform.java @@ -0,0 +1,12 @@ +package org.meteoinfo.geometry.graphic; + +import org.meteoinfo.common.PointD; + +public abstract class Transform { + + public abstract boolean isValid(); + + public abstract PointD transform(double x, double y); + + public abstract Transform inverted(); +} diff --git a/meteoinfo-lab/milconfig.xml b/meteoinfo-lab/milconfig.xml index 46f4d3dc..4bae7354 100644 --- a/meteoinfo-lab/milconfig.xml +++ b/meteoinfo-lab/milconfig.xml @@ -1,32 +1,32 @@ - - - - - - - + - - - + + + + + + + + + - - - + + + - - - + + + diff --git a/meteoinfo-lab/pylib/mipylib/meteolib/calc/kinematics$py.class b/meteoinfo-lab/pylib/mipylib/meteolib/calc/kinematics$py.class index 7684bad9..495eb048 100644 Binary files a/meteoinfo-lab/pylib/mipylib/meteolib/calc/kinematics$py.class and b/meteoinfo-lab/pylib/mipylib/meteolib/calc/kinematics$py.class differ diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/_axes$py.class b/meteoinfo-lab/pylib/mipylib/plotlib/_axes$py.class index 1a6b2219..78f6f845 100644 Binary files a/meteoinfo-lab/pylib/mipylib/plotlib/_axes$py.class and b/meteoinfo-lab/pylib/mipylib/plotlib/_axes$py.class differ diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/_axes.py b/meteoinfo-lab/pylib/mipylib/plotlib/_axes.py index c5a8e731..2e7a4c91 100644 --- a/meteoinfo-lab/pylib/mipylib/plotlib/_axes.py +++ b/meteoinfo-lab/pylib/mipylib/plotlib/_axes.py @@ -34,7 +34,7 @@ from mipylib.geolib.milayer import MILayer, MIXYListData import plotutil import colors import mipylib.miutil as miutil -from .graphic import Line2D, Artist +from .graphic import Line2D, Artist, Point2DCollection __all__ = ['Axes', 'PolarAxes'] @@ -1087,12 +1087,12 @@ class Axes(object): #self._axes.setAutoExtent() self.stale = True - def add_graphic(self, graphic, projection=None, zorder=None): + def add_graphic(self, graphic, transform=None, zorder=None): """ Add a graphic :param graphic: (*Graphic*) The graphic to be added. - :param projection: (*Projection*) The projection. + :param transform: (*Transform*) The transform. Default is `None`. :param zorder: (*int*) Z order of the graphic. Default is `None` that the graphic added to the end. """ @@ -1103,16 +1103,16 @@ class Axes(object): if zorder > self.num_graphics(): zorder = self.num_graphics() - if projection is None: + if transform is None: if zorder is None: rGraphic = self._axes.addGraphic(graphic) else: rGraphic = self._axes.addGraphic(zorder, graphic) else: if zorder is None: - rGraphic = self._axes.addGraphic(graphic, projection) + rGraphic = self._axes.addGraphic(graphic, transform) else: - rGraphic = self._axes.addGraphic(zorder, graphic, projection) + rGraphic = self._axes.addGraphic(zorder, graphic, transform) #self._axes.setAutoExtent() self.stale = True @@ -1578,8 +1578,8 @@ class Axes(object): for i in range(0, n): ls.getLegendBreaks()[i].setSize(s[i]) # Create graphics - graphics = GraphicFactory.createPoints(xdata, ydata, c.asarray(), ls) - + #graphics = GraphicFactory.createPoints(xdata, ydata, c.asarray(), ls) + graphics = Point2DCollection(xdata, ydata, c._array, legend=ls) else: alpha = kwargs.pop('alpha', None) colors = plotutil.getcolors(c, alpha) @@ -1614,8 +1614,8 @@ class Axes(object): plotutil.setpointlegendbreak(npb, **kwargs) pbs.append(npb) # Create graphics - graphics = GraphicFactory.createPoints(xdata, ydata, pbs) - graphics = Point2DCollection(x._array, y._array, legend=ls.getLegendBreak(0)) + #graphics = GraphicFactory.createPoints(xdata, ydata, pbs) + graphics = Point2DCollection(xdata, ydata, legend=pbs) antialias = kwargs.pop('antialias', None) if antialias is not None: diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/_mapaxes$py.class b/meteoinfo-lab/pylib/mipylib/plotlib/_mapaxes$py.class index 3f104e26..50e1fd80 100644 Binary files a/meteoinfo-lab/pylib/mipylib/plotlib/_mapaxes$py.class and b/meteoinfo-lab/pylib/mipylib/plotlib/_mapaxes$py.class differ diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/_mapaxes.py b/meteoinfo-lab/pylib/mipylib/plotlib/_mapaxes.py index 302dcf76..82085d98 100644 --- a/meteoinfo-lab/pylib/mipylib/plotlib/_mapaxes.py +++ b/meteoinfo-lab/pylib/mipylib/plotlib/_mapaxes.py @@ -8,6 +8,7 @@ import os import numbers +import functools from org.meteoinfo.chart import ChartScaleBar, ChartNorthArrow from org.meteoinfo.chart.plot import GridLabelPosition @@ -19,7 +20,7 @@ from org.meteoinfo.geo.io import GraphicUtil from org.meteoinfo.geometry.legend import BreakTypes, LegendScheme, LegendType, LegendManage from org.meteoinfo.geometry.shape import Shape, PolylineShape, PolygonShape, ShapeTypes from org.meteoinfo.geometry.graphic import Graphic -from org.meteoinfo.projection import ProjectionInfo +from org.meteoinfo.projection import ProjectionInfo, GeoTransform from org.meteoinfo.common import Extent from org.meteoinfo.geo.layer import LayerTypes, WebMapLayer from org.meteoinfo.data.mapdata.webmap import WebMapProvider, DefaultTileFactory, TileFactoryInfo @@ -40,6 +41,20 @@ import mipylib.miutil as miutil __all__ = ['MapAxes','WebMapProvider'] + +def _add_transform(func): + """A decorator that adds and validates the transform keyword argument.""" + @functools.wraps(func) + def wrapper(self, *args, **kwargs): + data_proj = kwargs.get('transform', None) + if data_proj is None: + data_proj = kwargs.pop('proj', None) + if data_proj is not None: + transform = GeoTransform(data_proj, self.projection) + kwargs['transform'] = transform + return func(self, *args, **kwargs) + return wrapper + ############################################## class MapAxes(Axes): """ @@ -93,7 +108,7 @@ class MapAxes(Axes): if not cutoff is None: projinfo.setCutoff(cutoff) self._axes.setProjInfo(projinfo) - self.proj = self._axes.getProjInfo() + self.projection = self._axes.getProjInfo() xyscale = kwargs.pop('xyscale', 1) self._axes.setAspect(xyscale) @@ -123,7 +138,7 @@ class MapAxes(Axes): :returns: (*boolean*) Is lonlat projection or not. """ - return self.proj.isLonLat() + return self.projection.isLonLat() def add_layer(self, layer, zorder=None, select=None): """ @@ -386,7 +401,7 @@ class MapAxes(Axes): :param z: (*float*) Z coordinate - only used for 3-D axes. """ if not self._axes.isLonLatMap(): - x, y = migeo.project(x, y, toproj=self.proj) + x, y = migeo.project(x, y, toproj=self.projection) rect = self._axes.getPositionArea() r = self._axes.projToScreen(x, y, rect) @@ -530,7 +545,9 @@ class MapAxes(Axes): if antialias is not None: graphics.setAntiAlias(antialias) - graphics = self.add_graphic(graphics, projection=layer.proj, zorder=zorder) + transform = GeoTransform(layer.proj, self.projection) + graphics.setTransform(transform) + graphics = self.add_graphic(graphics, zorder=zorder) graphics.setVisible(visible) return GeoGraphicCollection(graphics) else: @@ -587,21 +604,15 @@ class MapAxes(Axes): if graphic.getNumGraphics() == 1: graphic = graphic.getGraphicN(0) - if graphic.isCollection(): - if self.islonlat(): - self._axes.addGraphics(graphic) - else: - graphic = self._axes.addGraphics(graphic, migeo.projinfo()) - else: - if self.islonlat(): - self._axes.addGraphic(graphic) - else: - graphic = self._axes.addGraphic(graphic, migeo.projinfo()) + transform = GeoTransform(migeo.projinfo(), self.projection) + graphic.setTransform(transform) + graphic = self._axes.addGraphic(graphic) graphic.setVisible(visible) return graphic - + + @_add_transform def plot(self, *args, **kwargs): """ Plot lines and/or markers to the map. @@ -615,7 +626,7 @@ class MapAxes(Axes): :returns: (*VectorLayer*) Line VectorLayer. """ fill_value = kwargs.pop('fill_value', -9999.0) - proj = kwargs.pop('proj', migeo.projinfo()) + transform = kwargs.pop('transform', None) is_lonlat = proj.isLonLat() n = len(args) xdatalist = [] @@ -735,7 +746,8 @@ class MapAxes(Axes): graphic = GraphicFactory.createLineString(xdata, ydata, lines[0], iscurve) else: #>1 graphic = GraphicFactory.createLineString(xdata, ydata, lines, iscurve) - graphic = self.add_graphic(graphic, proj) + graphic.transform = transform + graphic = self.add_graphic(graphic) graphics.append(graphic) else: for i in range(0, snum): @@ -743,14 +755,16 @@ class MapAxes(Axes): xdata = plotutil.getplotdata(xdatalist[i]) ydata = plotutil.getplotdata(ydatalist[i]) graphic = GraphicFactory.createLineString(xdata, ydata, lines[i], iscurve) - graphic = self.add_graphic(graphic, proj) + graphic.transform = transform + graphic = self.add_graphic(graphic) graphics.append(graphic) else: xdata = plotutil.getplotdata(xdatalist[0]) ydata = plotutil.getplotdata(ydatalist[0]) zdata = plotutil.getplotdata(cdata) graphic = GraphicFactory.createLineString(xdata, ydata, zdata, ls, iscurve) - graphic = self.add_graphic(graphic, proj) + graphic.transform = transform + graphic = self.add_graphic(graphic) graphics.append(graphic) antialias = kwargs.pop('antialias', None) @@ -764,6 +778,7 @@ class MapAxes(Axes): return graphics[0] + @_add_transform def scatter(self, *args, **kwargs): """ Make a scatter plot on a map. @@ -783,10 +798,10 @@ class MapAxes(Axes): :param edge: (*boolean*) Draw edge of markers or not. Default is True. :param facecolor: (*Color*) Fill color of markers. Default is black. :param edgecolor: (*Color*) Edge color of markers. Default is black. - :param proj: (*ProjectionInfo*) Map projection of the data. Default is None. + :param transform: (*ProjectionInfo*) Map projection transform of the data. Default is same with the axes. :param zorder: (*int*) Z-order of created layer for display. - :returns: (*VectoryLayer*) Point VectoryLayer. + :returns: (*graphic collection*) Graphic collection. """ n = len(args) if n == 1: @@ -836,14 +851,16 @@ class MapAxes(Axes): ls = plotutil.getlegendscheme(args, a.min(), a.max(), **kwargs) ls = plotutil.setlegendscheme_point(ls, **kwargs) - proj = kwargs.pop('proj', migeo.projinfo()) # Create graphics if a.ndim == 0: - graphics = GraphicFactory.createPoints(x._array, y._array, ls.getLegendBreak(0)) - #graphics = Point2DCollection(x._array, y._array, legend=ls.getLegendBreak(0)) + #graphics = GraphicFactory.createPoints(x._array, y._array, ls.getLegendBreak(0)) + graphics = Point2DCollection(x._array, y._array, legend=ls.getLegendBreak(0)) else: - graphics = GraphicFactory.createPoints(x._array, y._array, a._array, ls) - #graphics = Point2DCollection(x._array, y._array, a._array, ls) + #graphics = GraphicFactory.createPoints(x._array, y._array, a._array, ls) + graphics = Point2DCollection(x._array, y._array, a._array, ls) + + transform = kwargs.pop('transform', None) + graphics.transform = transform antialias = kwargs.pop('antialias', None) if antialias is not None: @@ -851,7 +868,7 @@ class MapAxes(Axes): visible = kwargs.pop('visible', True) zorder = kwargs.pop('zorder', None) - graphics = self.add_graphic(graphics, projection=proj, zorder=zorder) + graphics = self.add_graphic(graphics, zorder=zorder) self.set_draw_extent(graphics.getExtent()) graphics.setVisible(visible) @@ -872,13 +889,14 @@ class MapAxes(Axes): :param coordinates=['axes'|'figure'|'data'|'inches']: (*string*) Coordinate system and units for *X, Y*. 'axes' and 'figure' are normalized coordinate system with 0,0 in the lower left and 1,1 in the upper right, 'data' are the axes data coordinates (Default value); 'inches' is - position in the figure in inches, with 0,0 at the lower left corner. + position in the figure in inches, with 0,0 in the lower left corner. """ ctext = plotutil.text(x, y, s, **kwargs) islonlat = kwargs.pop('islonlat', True) self._axes.addText(ctext, islonlat) return ctext - + + @_add_transform def contour(self, *args, **kwargs): """ Plot contours on the map. @@ -922,7 +940,8 @@ class MapAxes(Axes): graphics = GraphicFactory.createContourLines(x.asarray(), y.asarray(), a.asarray(), ls, smooth) - proj = kwargs.pop('proj', migeo.projinfo()) + transform = kwargs.pop('transform', None) + graphics.transform = transform # Add graphics antialias = kwargs.pop('antialias', None) @@ -931,12 +950,13 @@ class MapAxes(Axes): visible = kwargs.pop('visible', True) zorder = kwargs.pop('zorder', None) - graphics = self.add_graphic(graphics, projection=proj, zorder=zorder) + graphics = self.add_graphic(graphics, zorder=zorder) self.set_draw_extent(graphics.getExtent()) graphics.setVisible(visible) return graphics - + + @_add_transform def contourf(self, *args, **kwargs): """ Plot filled contours on the map. @@ -984,7 +1004,8 @@ class MapAxes(Axes): a, x, y = np.griddata((x,y), a, **griddata_props) graphics = GraphicFactory.createContourPolygons(x.asarray(), y.asarray(), a.asarray(), ls, smooth) - proj = kwargs.pop('proj', migeo.projinfo()) + transform = kwargs.pop('transform', None) + graphics.transform = transform # Add graphics antialias = kwargs.pop('antialias', None) @@ -995,12 +1016,13 @@ class MapAxes(Axes): zorder = kwargs.pop('zorder', None) if zorder is None: zorder = 0 - graphics = self.add_graphic(graphics, projection=proj, zorder=zorder) + graphics = self.add_graphic(graphics, zorder=zorder) self.set_draw_extent(graphics.getExtent()) graphics.setVisible(visible) return graphics - + + @_add_transform def imshow(self, *args, **kwargs): """ Display an image on the map. @@ -1066,7 +1088,7 @@ class MapAxes(Axes): visible = kwargs.pop('visible', True) interpolation = kwargs.pop('interpolation', None) - proj = kwargs.pop('proj', migeo.projinfo()) + transform = kwargs.pop('transform', None) if isrgb: if isinstance(rgbdata, (list, tuple)): rgbd = [] @@ -1120,8 +1142,8 @@ class MapAxes(Axes): if cb.isNoData(): cb.setColor(plotutil.getcolor(fill_color)) - if not proj.equals(self.proj): - arr, x, y = migeo.reproject(arr, x, y, fromproj=proj, toproj=self.proj) + if not transform.equals(self.projection): + arr, x, y = migeo.reproject(arr, x, y, fromproj=transform, toproj=self.projection) extent = [x[0],x[-1],y[0],y[-1]] igraphic = GraphicFactory.createImage(arr._array, x._array, y._array, ls, extent) @@ -1142,7 +1164,8 @@ class MapAxes(Axes): gridline.setTop(True) return igraphic - + + @_add_transform def pcolor(self, *args, **kwargs): """ Create a pseudocolor plot of a 2-D array in a MapAxes. @@ -1165,7 +1188,6 @@ class MapAxes(Axes): :returns: (*VectoryLayer*) Polygon VectoryLayer created from array data. """ - proj = kwargs.pop('proj', migeo.projinfo()) n = len(args) if n <= 2: a = args[0] @@ -1186,26 +1208,29 @@ class MapAxes(Axes): if not kwargs.has_key('edgecolor'): kwargs['edgecolor'] = None plotutil.setlegendscheme(ls, **kwargs) - - if proj is None or proj.isLonLat(): + + transform = kwargs.pop('transform', None) + if transform is None or transform.isLonLat(): lonlim = 90 else: lonlim = 0 #x, y = np.project(x, y, toproj=proj) graphics = GraphicFactory.createPColorPolygons(x.asarray(), y.asarray(), a.asarray(), ls) + graphics.transform = transform antialias = kwargs.pop('antialias', None) if antialias is not None: graphics.setAntiAlias(antialias) visible = kwargs.pop('visible', True) zorder = kwargs.pop('zorder', None) - graphics = self.add_graphic(graphics, projection=proj, zorder=zorder) + graphics = self.add_graphic(graphics, zorder=zorder) self.set_draw_extent(graphics.getExtent()) graphics.setVisible(visible) return graphics - + + @_add_transform def gridshow(self, *args, **kwargs): """ Create a grid plot of a 2-D array in a MapAxes. @@ -1251,17 +1276,20 @@ class MapAxes(Axes): if antialias is not None: graphics.setAntiAlias(antialias) - proj = kwargs.pop('proj', migeo.projinfo()) + transform = kwargs.pop('transform', None) + graphics.transform = transform + visible = kwargs.pop('visible', True) zorder = kwargs.pop('zorder', None) if zorder is None: zorder = 0 - graphics = self.add_graphic(graphics, projection=proj, zorder=zorder) + graphics = self.add_graphic(graphics, zorder=zorder) self.set_draw_extent(graphics.getExtent()) graphics.setVisible(visible) return graphics - + + @_add_transform def quiver(self, *args, **kwargs): """ Plot a 2-D field of quiver in a map. @@ -1285,7 +1313,7 @@ class MapAxes(Axes): """ cmap = plotutil.getcolormap(**kwargs) fill_value = kwargs.pop('fill_value', -9999.0) - proj = kwargs.pop('proj', migeo.projinfo()) + transform = kwargs.pop('transform', None) order = kwargs.pop('order', None) isuv = kwargs.pop('isuv', True) n = len(args) @@ -1342,7 +1370,8 @@ class MapAxes(Axes): x, y = np.meshgrid(x, y) graphics = GraphicFactory.createArrows(x._array, y._array, u._array, v._array, cdata, ls, isuv) - + graphics.transform = transform + # Add graphics antialias = kwargs.pop('antialias', None) if antialias is not None: @@ -1354,12 +1383,13 @@ class MapAxes(Axes): visible = kwargs.pop('visible', True) zorder = kwargs.pop('zorder', None) - graphics = self.add_graphic(graphics, projection=proj, zorder=zorder) + graphics = self.add_graphic(graphics, zorder=zorder) self.set_draw_extent(graphics.getExtent()) graphics.setVisible(visible) return graphics - + + @_add_transform def barbs(self, *args, **kwargs): """ Plot a 2-D field of barbs in a map. @@ -1447,15 +1477,18 @@ class MapAxes(Axes): if avoidcoll is not None: graphics.setAvoidCollision(avoidcoll) - proj = kwargs.pop('proj', migeo.projinfo()) + transform = kwargs.pop('transform', None) + graphics.transform = transform + visible = kwargs.pop('visible', True) zorder = kwargs.pop('zorder', None) - graphics = self.add_graphic(graphics, projection=proj, zorder=zorder) + graphics = self.add_graphic(graphics, zorder=zorder) self.set_draw_extent(graphics.getExtent()) graphics.setVisible(visible) return graphics - + + @_add_transform def streamplot(self, *args, **kwargs): """ Plot streamline in a map. @@ -1475,7 +1508,6 @@ class MapAxes(Axes): :returns: (*VectoryLayer*) Created streamline VectoryLayer. """ - proj = kwargs.pop('proj', migeo.projinfo()) isuv = kwargs.pop('isuv', True) density = kwargs.pop('density', 4) n = len(args) @@ -1528,12 +1560,15 @@ class MapAxes(Axes): visible = kwargs.pop('visible', True) zorder = kwargs.pop('zorder', None) - graphics = self.add_graphic(graphics, projection=proj, zorder=zorder) + transform = kwargs.pop('transform', None) + graphics.transform = transform + graphics = self.add_graphic(graphics, zorder=zorder) self.set_draw_extent(graphics.getExtent()) graphics.setVisible(visible) return graphics - + + @_add_transform def stationmodel(self, smdata, **kwargs): """ Plot station model data on the map. @@ -1547,13 +1582,14 @@ class MapAxes(Axes): :returns: (*graphics*) Station model graphics. """ - proj = kwargs.pop('proj', migeo.projinfo()) + transform = kwargs.pop('transform', None) size = kwargs.pop('size', 12) surface = kwargs.pop('surface', True) color = kwargs.pop('color', 'b') color = plotutil.getcolor(color) ls = LegendManage.createSingleSymbolLegendScheme(ShapeTypes.POINT, color, size) graphics = GraphicFactory.createStationModel(smdata, ls, surface) + graphics.transform = transform # Add graphics antialias = kwargs.pop('antialias', None) @@ -1566,7 +1602,7 @@ class MapAxes(Axes): visible = kwargs.pop('visible', True) zorder = kwargs.pop('zorder', None) - graphics = self.add_graphic(graphics, projection=proj, zorder=zorder) + graphics = self.add_graphic(graphics, zorder=zorder) self.set_draw_extent(graphics.getExtent()) graphics.setVisible(visible) diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/graphic/artist.py b/meteoinfo-lab/pylib/mipylib/plotlib/graphic/artist.py index 9c210209..3ecfc882 100644 --- a/meteoinfo-lab/pylib/mipylib/plotlib/graphic/artist.py +++ b/meteoinfo-lab/pylib/mipylib/plotlib/graphic/artist.py @@ -1,4 +1,5 @@ from abc import ABCMeta, abstractmethod +from org.meteoinfo.geometry.graphic import Graphic __all__ = ['Artist'] @@ -21,6 +22,7 @@ class Artist(object): self._stale = True self.stale_callback = None self._animated = False + self._transform = None @property def axes(self): @@ -57,4 +59,14 @@ class Artist(object): return if val and self.stale_callback is not None: - self.stale_callback(self, val) \ No newline at end of file + self.stale_callback(self, val) + + @property + def transform(self): + return self._transform + + @transform.setter + def transform(self, val): + self._transform = val + if isinstance(self, Graphic): + self.setTransform(val) diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/graphic/collections.py b/meteoinfo-lab/pylib/mipylib/plotlib/graphic/collections.py index 050620bc..8d1e3942 100644 --- a/meteoinfo-lab/pylib/mipylib/plotlib/graphic/collections.py +++ b/meteoinfo-lab/pylib/mipylib/plotlib/graphic/collections.py @@ -82,3 +82,84 @@ class Point2DCollection(Collection, Point2DGraphicCollection): Point2DGraphicCollection.__init__(self, self._x._array, self._y._array, legend) else: Point2DGraphicCollection.__init__(self, self._x._array, self._y._array, self._cdata._array, legend) + + @property + def visible(self): + """ + The artist is visible or not. + """ + return self.isVisible() + + @visible.setter + def visible(self, val): + self.setVisible(val) + self.stale = True + + @property + def xdata(self): + """ + Return the xdata. + + :return: (*array*) xdata. + """ + return self._x + + @xdata.setter + def xdata(self, xdata): + """ + Set the xdata. + + :param xdata: (*array*) The xdata. + """ + self._x = xdata + self.setXData(xdata._array) + self.stale = True + + @property + def ydata(self): + """ + Return the ydata. + + :return: (*array*) ydata. + """ + return self._y + + @ydata.setter + def ydata(self, ydata): + """ + Set the ydata. + + :param ydata: (*array*) The ydata. + """ + self._y = ydata + self.setYData(ydata._array) + self.stale = True + + @property + def data(self): + """ + Get x, y data. + + :return: x, y data. + """ + return (self._x, self._y) + + @data.setter + def data(self, *args): + """ + Set x, y data. + + :param xdata: (*array*) X data. + :param ydata: (*array*) Y data. + """ + if len(args) == 1: + xdata = args[0][0] + ydata = args[0][1] + else: + xdata = args[0] + ydata = args[1] + + self._x = xdata + self._y = ydata + self.setData(xdata._array, ydata._array) + self.stale = True diff --git a/meteoinfo-projection/src/main/java/org/meteoinfo/projection/GeoTransform.java b/meteoinfo-projection/src/main/java/org/meteoinfo/projection/GeoTransform.java new file mode 100644 index 00000000..a628f47d --- /dev/null +++ b/meteoinfo-projection/src/main/java/org/meteoinfo/projection/GeoTransform.java @@ -0,0 +1,60 @@ +package org.meteoinfo.projection; + +import org.locationtech.proj4j.BasicCoordinateTransform; +import org.locationtech.proj4j.CoordinateTransform; +import org.locationtech.proj4j.ProjCoordinate; +import org.meteoinfo.common.PointD; +import org.meteoinfo.geometry.graphic.Transform; + +public class GeoTransform extends Transform { + + protected CoordinateTransform coordinateTransform; + protected ProjectionInfo sourceProj; + protected ProjectionInfo targetProj; + + /** + * Constructor + * @param source Source projection info + * @param target Target projection info + */ + public GeoTransform(ProjectionInfo source, ProjectionInfo target) { + this.sourceProj = source; + this.targetProj = target; + this.coordinateTransform = new BasicCoordinateTransform(source.getCoordinateReferenceSystem(), + target.getCoordinateReferenceSystem()); + } + + /** + * Get source projection + * @return Source projection + */ + public ProjectionInfo getSourceProj() { + return this.sourceProj; + } + + /** + * Get target projection + * @return Target projection + */ + public ProjectionInfo getTargetProj() { + return this.targetProj; + } + + @Override + public boolean isValid() { + return !sourceProj.equals(targetProj); + } + + @Override + public PointD transform(double x, double y) { + ProjCoordinate s = new ProjCoordinate(x, y); + ProjCoordinate t = new ProjCoordinate(); + this.coordinateTransform.transform(s, t); + return new PointD(t.x, t.y); + } + + @Override + public Transform inverted() { + return new GeoTransform(this.targetProj, this.sourceProj); + } +} diff --git a/meteoinfo-projection/src/main/java/org/meteoinfo/projection/ProjectionUtil.java b/meteoinfo-projection/src/main/java/org/meteoinfo/projection/ProjectionUtil.java index f311cc87..86b162a0 100644 --- a/meteoinfo-projection/src/main/java/org/meteoinfo/projection/ProjectionUtil.java +++ b/meteoinfo-projection/src/main/java/org/meteoinfo/projection/ProjectionUtil.java @@ -946,6 +946,43 @@ public class ProjectionUtil { * @return Projected graphic */ public static Graphic projectClipGraphic(Graphic graphic, ProjectionInfo fromProj, ProjectionInfo toProj) { + if (graphic instanceof GraphicCollection) { + try { + List graphics = new ArrayList<>(); + for (Graphic aGraphic : ((GraphicCollection) graphic).getGraphics()) { + List shapes = projectClipShape(aGraphic.getShape(), fromProj, toProj); + if (shapes != null && shapes.size() > 0) { + aGraphic.setShape(shapes.get(0)); + graphics.add(aGraphic); + } + } + ((GraphicCollection) graphic).setGraphics(graphics); + + return graphic; + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + } else { + List shapes = projectClipShape(graphic.getShape(), fromProj, toProj); + if (shapes != null && shapes.size() > 0) { + graphic.setShape(shapes.get(0)); + return graphic; + } else { + return null; + } + } + } + + /** + * Project graphic + * + * @param graphic The graphic + * @param fromProj From projection + * @param toProj To projection + * @return Projected graphic + */ + public static Graphic projectClipGraphic_old(Graphic graphic, ProjectionInfo fromProj, ProjectionInfo toProj) { if (graphic instanceof GraphicCollection) { try { Graphic newGCollection = graphic.getClass().getDeclaredConstructor().newInstance();