From a5d61be3add537b7ba797dda272a89a7ea9cff3c Mon Sep 17 00:00:00 2001 From: wyq Date: Thu, 30 Jun 2022 16:42:37 +0800 Subject: [PATCH] update geoshow function to support projected image layer in 3D axes --- .../chart/graphic/GraphicFactory.java | 236 ++++++++++++++++++ .../chart/graphic/GraphicProjectionUtil.java | 40 +++ .../org/meteoinfo/chart/jogl/JOGLUtil.java | 39 --- .../org/meteoinfo/chart/jogl/MapPlot3D.java | 5 +- .../chart/render/jogl/MeshRender.java | 6 + meteoinfo-lab/milconfig.xml | 40 ++- .../pylib/mipylib/plotlib/_axes3dgl$py.class | Bin 91217 -> 91656 bytes .../pylib/mipylib/plotlib/_axes3dgl.py | 22 +- .../info/LambertConformalConic.java | 2 +- 9 files changed, 321 insertions(+), 69 deletions(-) create mode 100644 meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicProjectionUtil.java diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicFactory.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicFactory.java index 5c47d41c..b927da05 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicFactory.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicFactory.java @@ -11,6 +11,7 @@ import org.meteoinfo.chart.ChartText3D; import org.meteoinfo.chart.jogl.mc.CallbackMC; import org.meteoinfo.chart.jogl.mc.MarchingCubes; import org.meteoinfo.chart.jogl.pipe.PipeShape; +import org.meteoinfo.chart.shape.TextureShape; import org.meteoinfo.common.*; import org.meteoinfo.common.colors.ColorMap; import org.meteoinfo.data.GridArray; @@ -41,6 +42,7 @@ import org.meteoinfo.ndarray.*; import org.meteoinfo.ndarray.math.ArrayMath; import org.meteoinfo.ndarray.math.ArrayUtil; import org.meteoinfo.ndarray.util.BigDecimalUtil; +import org.meteoinfo.projection.ProjectionInfo; import wcontour.Contour; import wcontour.global.Point3D; import wcontour.global.PolyLine; @@ -49,6 +51,7 @@ import wcontour.global.PolyLine3D; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; +import java.io.IOException; import java.util.List; import java.util.*; @@ -3292,6 +3295,45 @@ public class GraphicFactory { return graphics; } + /** + * Create Texture + * + * @param gl GL2 + * @param layer Image layer + * @param offset Offset of z axis + * @param xshift X shift - to shift the grahpics in x direction, normally + * for map in 180 - 360 degree east + * @param interpolation Interpolation + * @return Graphics + * @throws IOException + */ + public static GraphicCollection createTexture(ImageLayer layer, double offset, double xshift, + String interpolation) throws IOException { + GraphicCollection3D graphics = new GraphicCollection3D(); + graphics.setFixZ(true); + graphics.setZDir("z"); + graphics.setZValue(offset); + TextureShape ishape = new TextureShape(); + ishape.setFileName(layer.getFileName()); + ishape.setImage(layer.getImage()); + Extent extent = layer.getExtent(); + Extent3D ex3 = new Extent3D(extent.minX + xshift, extent.maxX + xshift, extent.minY, extent.maxY, offset, offset); + List coords = new ArrayList<>(); + coords.add(new PointZ(extent.minX + xshift, extent.minY, offset)); + coords.add(new PointZ(extent.maxX + xshift, extent.minY, offset)); + coords.add(new PointZ(extent.maxX + xshift, extent.maxY, offset)); + coords.add(new PointZ(extent.minX + xshift, extent.maxY, offset)); + ishape.setExtent(ex3); + ishape.setCoords(coords); + Graphic gg = new Graphic(ishape, new ColorBreak()); + if (interpolation != null) { + ((ImageShape) gg.getShape()).setInterpolation(interpolation); + } + graphics.add(gg); + + return graphics; + } + /** * Create contour lines * @@ -7281,6 +7323,200 @@ public class GraphicFactory { return surfaceGraphic; } + /** + * Create surface graphic + * + * @param layer Image layer + * @param offset Offset of z axis + * @param xShift X shift - to shift the graphics in x direction, normally + * for map in 180 - 360 degree east + * @param nLon Number of longitude + * @param nLat Number of latitude + * @return Graphics + * @throws IOException + */ + public static MeshGraphic geoSurface(ImageLayer layer, double offset, double xShift, + int nLon, int nLat) throws IOException { + Extent extent = layer.getExtent(); + Array lon = ArrayUtil.lineSpace(extent.minX + xShift, extent.maxX + xShift, nLon + 1, true); + Array lat = ArrayUtil.lineSpace(extent.minY, extent.maxY, nLat + 1, true); + lat = lat.flip(0).copy(); + Array[] lonlat = ArrayUtil.meshgrid(lon, lat); + lon = lonlat[0]; + lat = lonlat[1]; + Array alt = ArrayUtil.zeros(lon.getShape(), DataType.FLOAT); + alt = ArrayMath.add(alt, offset); + LegendScheme ls = LegendManage.createSingleSymbolLegendScheme(ShapeTypes.POLYGON, Color.cyan, 1); + ((PolygonBreak) ls.getLegendBreak(0)).setDrawOutline(false); + ((PolygonBreak) ls.getLegendBreak(0)).setOutlineColor(Color.white); + + MeshGraphic graphic = GraphicFactory.surface(lon, lat, alt, ls); + graphic.setImage(layer.getImage()); + + return graphic; + } + + /** + * Create surface graphic + * + * @param layer Image layer + * @param offset Offset of z axis + * @param xShift X shift - to shift the graphics in x direction, normally + * for map in 180 - 360 degree east + * @param nLon Number of longitude + * @param nLat Number of latitude + * @param toProj Destination projection + * @return Graphics + * @throws IOException + */ + public static MeshGraphic geoSurface(ImageLayer layer, double offset, double xShift, + int nLon, int nLat, ProjectionInfo toProj) throws IOException { + Extent layerExtent = layer.getExtent(); + Extent extent = (Extent) layerExtent.clone(); + double width = extent.getWidth(); + double height = extent.getHeight(); + float cutoff = toProj.getCutoff(); + double cLon = toProj.getCenterLon(); + boolean extentChanged = false; + switch (toProj.getProjectionName()) { + case Lambert_Conformal_Conic: + if (extent.minY < cutoff) { + extent.minY = cutoff; + extentChanged = true; + } + break; + case North_Polar_Stereographic_Azimuthal: + if (extent.minY < cutoff) { + extent.minY = cutoff; + extentChanged = true; + } + break; + case South_Polar_Stereographic_Azimuthal: + if (extent.maxY > cutoff) { + extent.maxY = cutoff; + extentChanged = true; + } + break; + case Mercator: + if (extent.maxY > cutoff) { + extent.maxY = cutoff; + extentChanged = true; + } + if (extent.minY < -cutoff) { + extent.minY = -cutoff; + extentChanged = true; + } + break; + } + + if (cLon != 0) { + extent.minX = cLon - 180 + 0.1; + extent.maxX = cLon + 180 - 0.1; + extentChanged = true; + } + + BufferedImage image = layer.getImage(); + if (extentChanged) { + int x = (int) ((extent.minX - layerExtent.minX) / width * image.getWidth()); + int y = (int) ((layerExtent.maxY - extent.maxY) / height * image.getHeight()); + int w = (int) (extent.getWidth() / layerExtent.getWidth() * image.getWidth()); + int h = (int) (extent.getHeight() / layerExtent.getHeight() * image.getHeight()); + BufferedImage nImage = new BufferedImage(w, h, image.getType()); + if (cLon == 0) { + int[] rgb = new int[w * h]; + rgb = image.getRGB(x, y, w, h, rgb, 0, w); + nImage.setRGB(0, 0, w, h, rgb, 0, w); + } + else { + if (cLon > 0) { + int w1 = (int) ((layerExtent.maxX - extent.minX) / extent.getWidth() * w); + int[] rgb1 = new int[w1 * h]; + rgb1 = image.getRGB(x, y, w1, h, rgb1, 0, w1); + int[] rgb2 = new int[(w - w1) * h]; + rgb2 = image.getRGB(0, y, w - w1, h, rgb2, 0, w - w1); + nImage.setRGB(0, 0, w1, h, rgb1, 0, w1); + nImage.setRGB(w1, 0, w - w1, h, rgb2, 0, w - w1); + } else { + int w1 = (int) ((extent.maxX - layerExtent.minX) / extent.getWidth() * w); + int[] rgb1 = new int[w1 * h]; + rgb1 = image.getRGB(x, y, w1, h, rgb1, 0, w1); + int[] rgb2 = new int[(w - w1) * h]; + rgb2 = image.getRGB(x + w1, y, w - w1, h, rgb2, 0, w - w1); + nImage.setRGB(w - w1, 0, w1, h, rgb1, 0, w1); + nImage.setRGB(0, 0, w - w1, h, rgb2, 0, w - w1); + } + } + image = nImage; + } + + Array lon = ArrayUtil.lineSpace(extent.minX + xShift, extent.maxX + xShift, nLon + 1, true); + Array lat = ArrayUtil.lineSpace(extent.minY, extent.maxY, nLat + 1, true); + lat = lat.flip(0).copy(); + Array[] lonlat = ArrayUtil.meshgrid(lon, lat); + lon = lonlat[0]; + lat = lonlat[1]; + Array alt = ArrayUtil.zeros(lon.getShape(), DataType.FLOAT); + alt = ArrayMath.add(alt, offset); + LegendScheme ls = LegendManage.createSingleSymbolLegendScheme(ShapeTypes.POLYGON, Color.cyan, 1); + ((PolygonBreak) ls.getLegendBreak(0)).setDrawOutline(false); + ((PolygonBreak) ls.getLegendBreak(0)).setOutlineColor(Color.white); + + MeshGraphic graphic = GraphicFactory.surface(lon, lat, alt, ls); + graphic.setImage(image); + + return graphic; + } + + /** + * Create surface graphic + * + * @param layer Image layer + * @param offset Offset of z axis + * @param xShift X shift - to shift the graphics in x direction, normally + * for map in 180 - 360 degree east + * @param nLon Number of longitude + * @param nLat Number of latitude + * @param toProj Destination projection + * @param limits Image extent limitations + * @return Graphics + * @throws IOException + */ + public static MeshGraphic geoSurface(ImageLayer layer, double offset, double xShift, + int nLon, int nLat, ProjectionInfo toProj, List limits) throws IOException { + Extent layerExtent = layer.getExtent(); + Extent extent = new Extent(limits.get(0).doubleValue(), limits.get(1).doubleValue(), + limits.get(2).doubleValue(), limits.get(3).doubleValue()); + double width = layerExtent.getWidth(); + double height = layerExtent.getHeight(); + + BufferedImage image = layer.getImage(); + int x = (int) ((extent.minX - layerExtent.minX) / width * image.getWidth()); + int y = (int) ((layerExtent.maxY - extent.maxY) / height * image.getHeight()); + int w = (int)(extent.getWidth() / layerExtent.getWidth() * image.getWidth()); + int h = (int)(extent.getHeight() / layerExtent.getHeight() * image.getHeight()); + BufferedImage nImage = new BufferedImage(w, h, image.getType()); + Graphics2D g2 = nImage.createGraphics(); + g2.drawImage(image.getSubimage(x, y, w, h), 0, 0, null); + image = nImage; + + Array lon = ArrayUtil.lineSpace(extent.minX + xShift, extent.maxX + xShift, nLon + 1, true); + Array lat = ArrayUtil.lineSpace(extent.minY, extent.maxY, nLat + 1, true); + lat = lat.flip(0).copy(); + Array[] lonlat = ArrayUtil.meshgrid(lon, lat); + lon = lonlat[0]; + lat = lonlat[1]; + Array alt = ArrayUtil.zeros(lon.getShape(), DataType.FLOAT); + alt = ArrayMath.add(alt, offset); + LegendScheme ls = LegendManage.createSingleSymbolLegendScheme(ShapeTypes.POLYGON, Color.cyan, 1); + ((PolygonBreak) ls.getLegendBreak(0)).setDrawOutline(false); + ((PolygonBreak) ls.getLegendBreak(0)).setOutlineColor(Color.white); + + MeshGraphic graphic = GraphicFactory.surface(lon, lat, alt, ls); + graphic.setImage(image); + + return graphic; + } + /** * Create slice graphics * 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 new file mode 100644 index 00000000..33627cd2 --- /dev/null +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/graphic/GraphicProjectionUtil.java @@ -0,0 +1,40 @@ +package org.meteoinfo.chart.graphic; + +import org.locationtech.proj4j.CoordinateTransform; +import org.locationtech.proj4j.CoordinateTransformFactory; +import org.locationtech.proj4j.ProjCoordinate; +import org.meteoinfo.geometry.geoprocess.GeoComputation; +import org.meteoinfo.geometry.graphic.Graphic; +import org.meteoinfo.projection.ProjectionInfo; +import org.meteoinfo.projection.ProjectionNames; +import org.meteoinfo.projection.ProjectionUtil; +import org.meteoinfo.projection.Reproject; + +public class GraphicProjectionUtil extends ProjectionUtil { + /** + * Project graphic + * + * @param graphic The graphic + * @param fromProj From projection + * @param toProj To projection + * @return Projected graphic + */ + public static Graphic projectClipGraphic(Graphic graphic, ProjectionInfo fromProj, ProjectionInfo toProj) { + if (graphic instanceof MeshGraphic) { + CoordinateTransform trans = new CoordinateTransformFactory().createTransform(fromProj.getCoordinateReferenceSystem(), + toProj.getCoordinateReferenceSystem()); + float[] vertex = ((MeshGraphic) graphic).getVertexPosition(); + for (int i = 0; i < vertex.length; i+=3) { + ProjCoordinate p1 = new ProjCoordinate(vertex[i], vertex[i + 1]); + ProjCoordinate p2 = new ProjCoordinate(); + trans.transform(p1, p2); + vertex[i] = (float) p2.x; + vertex[i + 1] = (float) p2.y; + } + ((MeshGraphic) graphic).setVertexPosition(vertex); + return graphic; + } else { + return ProjectionUtil.projectClipGraphic(graphic, fromProj, toProj); + } + } +} diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/JOGLUtil.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/JOGLUtil.java index b168c62a..bb33b8af 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/JOGLUtil.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/JOGLUtil.java @@ -164,45 +164,6 @@ public class JOGLUtil { return cb.getColor().getRGBComponents(null); } - /** - * Create Texture - * - * @param gl GL2 - * @param layer Image layer - * @param offset Offset of z axis - * @param xshift X shift - to shift the grahpics in x direction, normally - * for map in 180 - 360 degree east - * @param interpolation Interpolation - * @return Graphics - * @throws IOException - */ - public static GraphicCollection createTexture(ImageLayer layer, double offset, double xshift, - String interpolation) throws IOException { - GraphicCollection3D graphics = new GraphicCollection3D(); - graphics.setFixZ(true); - graphics.setZDir("z"); - graphics.setZValue(offset); - TextureShape ishape = new TextureShape(); - ishape.setFileName(layer.getFileName()); - ishape.setImage(layer.getImage()); - Extent extent = layer.getExtent(); - Extent3D ex3 = new Extent3D(extent.minX + xshift, extent.maxX + xshift, extent.minY, extent.maxY, offset, offset); - List coords = new ArrayList<>(); - coords.add(new PointZ(extent.minX + xshift, extent.minY, offset)); - coords.add(new PointZ(extent.maxX + xshift, extent.minY, offset)); - coords.add(new PointZ(extent.maxX + xshift, extent.maxY, offset)); - coords.add(new PointZ(extent.minX + xshift, extent.maxY, offset)); - ishape.setExtent(ex3); - ishape.setCoords(coords); - Graphic gg = new Graphic(ishape, new ColorBreak()); - if (interpolation != null) { - ((ImageShape) gg.getShape()).setInterpolation(interpolation); - } - graphics.add(gg); - - return graphics; - } - /** * Create isosurface graphics * diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/MapPlot3D.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/MapPlot3D.java index 76969ebf..fce5360c 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/MapPlot3D.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/jogl/MapPlot3D.java @@ -3,6 +3,7 @@ package org.meteoinfo.chart.jogl; import com.jogamp.opengl.GL; import com.jogamp.opengl.GL2; import org.meteoinfo.chart.ChartText; +import org.meteoinfo.chart.graphic.GraphicProjectionUtil; import org.meteoinfo.chart.plot.MapGridLine; import org.meteoinfo.chart.plot.MapGridLine3D; import org.meteoinfo.common.*; @@ -64,7 +65,7 @@ public class MapPlot3D extends Plot3DGL { if (proj.equals(this.projInfo)) { super.addGraphic(graphic); } else { - Graphic nGraphic = ProjectionUtil.projectClipGraphic(graphic, proj, this.projInfo); + Graphic nGraphic = GraphicProjectionUtil.projectClipGraphic(graphic, proj, this.projInfo); super.addGraphic(nGraphic); } } @@ -81,7 +82,7 @@ public class MapPlot3D extends Plot3DGL { if (proj.equals(this.projInfo)) { super.addGraphic(index, graphic); } else { - Graphic nGraphic = ProjectionUtil.projectClipGraphic(graphic, proj, this.projInfo); + Graphic nGraphic = GraphicProjectionUtil.projectClipGraphic(graphic, proj, this.projInfo); super.addGraphic(index, nGraphic); } } diff --git a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/render/jogl/MeshRender.java b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/render/jogl/MeshRender.java index 68bf61dc..ca88a90d 100644 --- a/meteoinfo-chart/src/main/java/org/meteoinfo/chart/render/jogl/MeshRender.java +++ b/meteoinfo-chart/src/main/java/org/meteoinfo/chart/render/jogl/MeshRender.java @@ -111,6 +111,12 @@ public class MeshRender extends JOGLGraphicRender { gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, indexBuffer.capacity() * Integer.BYTES, indexBuffer, GL.GL_STATIC_DRAW); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0); } + + if (alwaysUpdateBuffers) { + texture = AWTTextureIO.newTexture(gl.getGLProfile(), meshGraphic.getImage(), true); + this.textureID = texture.getTextureObject(gl); + this.bindingTextures(); + } } void bindingTextures() { diff --git a/meteoinfo-lab/milconfig.xml b/meteoinfo-lab/milconfig.xml index d0b512a1..ca057c39 100644 --- a/meteoinfo-lab/milconfig.xml +++ b/meteoinfo-lab/milconfig.xml @@ -1,34 +1,32 @@ - - - - - - - - - - - + - + + + + + + + + + + + - - - - + + + - - - - + + + @@ -36,5 +34,5 @@
- + diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl$py.class b/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl$py.class index abc474acf967c3d3ddfaeabb56be813ad3d3b1a6..967b03bef59e7f8b84a49c522d5d2e3a21cf1236 100644 GIT binary patch delta 27547 zcmc(H33wF67H(H}b!U=H!c00VAq#{(2|LQZ$c_-eD7b(zgaFZyU=miv5fu>^6kBW& zQA9L%p|E0jS?TqIO2(JE zv~Em^m{3@nJEd%VenFv2>%kPGxM)&QMY&7sg}TX*EG(!fqQ;lFwQx2joQ>A{G9?VH z$|sgib7}o8Z^!472f4L@vhq@DR$N+u-MpGt5;T6cZ-p3WZw6&m99J|(fwo?b+by|LemZyz{=K1BtMdi5QaZCv>C@q;59VX6;sLzC*`B2g!Cybfo79Q@1hd7Rwnf> zXNrztXtJg-C8|8Bu%bVvZjisrX>M(poQD~e<(&<+@MiQhEx!!&cMDSz#l(o_=|$z~ z;|eF26&B=I6pnLgw=yNBum3ludlZ@!l}vDHx1oLExCt2U4!3rb?E21{DfTOJSLNZZ zd1}{taB1a}N=qvyy0rV4;>HwIlujwjmD=9#*6x_Ql&v0hRQj7eR+-p)C>#C+ZsnxH@`)45if|!MVz$d`*3(n;qp)HqYF*khG+%`k z{ctrk)Ag)ddt7$^4_qg8;?iEIeC?9EneSyxbiug%ihP&$Dwb_QSs^BB(4>5tij{I5 z4=Br@Jh7+%cXbt1z{1X*m_HfSRq~f#cnyAGSpTor-P%iX#Pt}F6m@AEnNm;Q8L4;U z_U&2a#=b)t+hV!L z!_a-|q#~@U&qXeOx=Z_lR_X*W>`S>)zrv^^iC+E6%1S}RudVMd&0^nLi9>P{zSn+; zP?l-OL6rRB$rJPQL|;EyvxX$}|HbHew7vKbwUhJ9DvAn<3t-)cq#alD@y)!Ylo!rXDdG)^1Z5Z)p~1K!!+*ZOq6qjDbZpAgB^2MW!Uf(o*ikx zbcz2kLP=@aBp0s-4RVmnFQ)Ff7ZnA?MU!3Jv_@QB;){bJ;qg)nhgc$3PM++bJ{Ag8 z>{D7?S~e8K<_$2_Q%j4dOhR|5SdXQX^9zb9WYaPd~wJy&G4Xe;?5W|OyPN*u-+QVJ6RlW8jPP(T*GeRrsMq$H~TYgey3FUX{<(U%(MmZ z<<^(itd4~lBpcbcEPqZ|`7U&KO%-QBewe02Ajbn1P!z4xR=75`#<^I|M(5>3OLrkHC~@f# z5G!!$ZX`u6JsPDGCb)DDR4dN+k?*9(plm!OV^KDinBvl5?y%F9PAaS@LzgghuA=fO zB}F&Fi_&53xVUn?0WM0|uRPo%mkvu8Ne&(y8R7Zpuyc{*RRjuPGqQmiIiFMeraIDn4afQX$k@c4JGrywdXBO>#lW89)cj>L@ZkCoH*M@TWRMrl; z{9?*=z;dAVu3v;q(Kt*;Cn|x1sds_UblQmYZmc_Hv8(AlP&h*r_7XWNy9BwJqO7mT zQCWXlU6L{SKx<-tj%RfQ8?6s>>x1=6DL2B}o}b2Ytz-EkU03KMBlOGlQC8oveO*`S zS7W8?*H{mX&0~4iiLnK)e0^+$ew|)mT~ko%n*f`rsMJM~TX{+5Zg8_gnVaNh*UMa~ zo0ZGljc#_c%$2*@?J_sT&F+=CX>Ml8+zdDK_k5F^JtNCzx!KDy=X0~w$oVkvY&Y8| z3vYL`w`6XPo9&jlxo-Bc%*}JN!!mb|n|&>F_qo}RGIzgQO_sR@Znde*S#EWP%q?`Q zKggU7M_=X^!K?Sl>|!{!GPeYNsLVY9mr~}QgqtXHPr;*;xo6;M$sD}A%`*27cs?@s z0{k4AdkId1%)J6PLFQoKB4n=8hwV#dU&9646BE=?yv@_W=Ma4xNsq9o#!t3{2 z!v}TYnK5&dc$JdKy6|kaMk~nl8r`kUladoe-Tk3;qfr;zVnOywUgdATptgwm8oP2O zyZTj($n-C))+IOA>#iuqXp9Y(h|ymuxs?0*nOfu;<-XNyY)+=B4ag=Hax%T@b5)9K zrLsI{o$}mOnX8l+vsY?EOl@#bG$$asvaaY%QyUQ!omwNRc|t{fE48_%HY+GQFCd#6 zD!Wp9(A4e?iarz&eY&pbVpFq&qDumzzt$G@(S<#4YDP?)oQuNbWq6Y(_1$9KZVrriSdp#8p zT^1De`EM={Gr1O!<>3L@FM_f)qS+>Q2Sr=gh#KwdiuN#hyP#+8Xz50DEy4g9Joyc_U5a7Ixjhy#45C=b4W`ZvXGbmD*Oh%y z`M%F&?1=UCt+z)X#o`ZB)M_P1uMZAPpPhz^M`kyTLkEh14ix1MIAqvos2l73*?p3z z2DTFa-UHl=S{OA^+xoV_j#>+~51|(Ok*Iy}HfP(#sC^8z*rP=4kG0x9h1#c4i+xMf zj=0^~_65|wh+02Ot(R_hj$4V^Rj9@OCfa)LaJF5C+V!Z#o+oOH?{K!=hT1n!i+xbk z?zy9sePQ*SlZ;upW=Q9p_T{t(32PWbPo2oAbpz87MODor~)E_g)^HN|p@^)k^VSQvGv9IjiJ3O0CvUCt)=WdU_aEQ{|#(`qUFczNDo7 z-H_SYpZ-gOjC`$mOrS-R*fYwA#C?8cR+j25JQJ~p&VZ^Oa(XJ%QCI&>K1ELR+sD@rm z=0dlo)}1=9V*PV6W7bzGo3it&6yG;2r%E}zNXds93#ma&G^$LkC&{`tD06Z$v4>S= z=V^JlnPDQwkwb?l(~a-B z9aK;sw_f=^-RgQcTvea2dVQB_^;)ktS}LZDG+GuU7*~YXhJ( zcicPc;jfddt{bB+CY{6%p}vBiUq#Pwp2=`2O7t|@vwJrB+AMk|FNk{HVl`Z3+9lD< zm3FzGM4M2&l?EirVDANckC@s>yG=6|AFFOVN6n^as(IraHQsaD#U00oM&D<7|NdAvBJ3$i#C5~E;ZOe%MQuG;#K66a% z25Z;0goLj|5v}zqhP4X@y#VoJ*8E@MTTQ}Z$i0f2TB*qBrgkG`Rx1l(U9}3V3xAVw zX!5ny^NptUzY)VXB}LU2YiVuy-e`*R=UWeSQq}J*?<QIl`llFC+V!dS zt(k0$bz=KvQK!yT%&k}7>dekqSu4}6`)+oHd(~=j`Ce=GtVDw)<1+rVZd#ch8MF(3 zS>txJi8|}-&3bajeX7Q+HgBhm(_k`i8^!$5GU|*n3;$QMFnBeVXJ;$;Ky8o5B?>2jyp-6p%2=nHf=ZvW za)hHY=!!HBDHT1Dd1a}}0O+w9*aB;@EP#Ab>+LweBUGY18^Mp5%>u>0h|PW27Un$2&n%A zP620tYT!@cFW@W!1$gnA25+Fdh?40rEgI$<$1l=+hg;Ho zyRheJ4RF6?U`h)qh3;O=IAJe*w9HIxHmHk;`F50H?<1kqbJ}<Z;le3{5H zVc^@lv#2;|-xFE(1(|mZHq&X|6ItsEvhG^jfOXf}1+BZ*URZanLydLUI>Nfgvx|gr zAAT%_b+Ss1WkhwRQ3J;P@nZ?B3nYfIZm{kBeQ;G#dts(s>p{X!_jwN8$Qal+x{omO zku|#jxwYt5n0L{_)JPU)U=4ZKS}&@O8DC}1Ji($P*!=-`I2051z(( z=m$F2sgd+6JV%YB-*`X$CIskLBD+LHbQ~&~KWg-}C_eW(fMt4AAdpLBClvYAyQRBItK3(JyMYsP)s& z$~%zGZi8FWncX2Mari(4n`3=@par|riu$G*n=2?mM7c{4Wu73)-C#s3c8?^0zdQRiDZhmxY^Q?Wnpe84LCb~#%h{Ex1OBiMrocrutpE{AnDem`X0|6Q9XXQcXo z^k`at)}7h*2#qyTwMVQ4pJlK|;T5!Gi>=R*f6V&p`((DnO8-11{xJ+!XXAfdL`#!? zh=_ecTzjl@^IvK$_&k|CX}$D$1GZFBahaeZ4mD2vkjxHR?&FQ+w%+sqVdqwURJvNP z97!uI0#B|7ZUBmbNk9ou3QPuW1j>MNUHxpjN`AjC_`Oc>d%fWI2Ep%*)RmLpn*_f%Q?ZlZTP}>>A|Q0~d)tNa`^_M~w>$X# zmgM)20KeZB{C+3E?{@{icagTW_`O^3`#s`!D-qFM==Qo#Q2&Df^*#eV0t zEFB8dN~ApQYK{DXq{IG{Ke@efn6pZJRe@s9+E|Aip_(E#zk6vY3^*&;~% zul>aTMiBoyLHzFp@qZA+KQ4&>0~*&M{*M9T|3rOzY7zg0ApS`~{1cM+KmWIg|BvHo z`dZNFb!*r0WFK624X(Qe*Ij!P*bck}z;(wHLPdKAco*0O>;~Qg;IV7)*zw#@(crOb z@YpqY>>50F4IaCOb6*W!y9TcvPZSmHFmMF;0)S6}_o5W-E8uHI8L54P%y+=|zz@K2 zfBGX*IQ1HwdJRs!NPhYvkHHg@&oaGS zp?MGSrcW3w;bK;|f*f^uu!88`e@KBYRFaIDA-t?*R>PmVt6Z~=Oikpd8T zBzSvKE2fcdW@8j}a5R{1o&0BP$Bv>zTuY76MGm3o85Rg{y!D zpn!v~JPdFF;Xowd2BLv_fCumbF@Oog0`WiskO$s*x!IuMn#OS0TY1LUR^U0Z$>;T6)T0Io7kMy0c;S-qtMn!9OApentM# zBx(dT37D7efAnCN1&qq&){;Mx*%j8>Kax{M)^x4Qv)`P|p#2yn49!)-&|Gb`Io&dG zwCL4uX#D4*(43CM``g6lJZtmmcy^uj@#!>{Z~cycDJ+5kqLo%)^*l4O@wl3C z{b$b3ke7?d#}jECJY7F*JP9ISeY!rIAg0h&9m^(KDb<0?qd)C%n_jc`!T zXF{qgyV**cA#G}BbURD5T>n=B^ZENo7>HsS+)%7-YUi=3fxw*OFbKE)wA>B6*)^v2 z4r|2OcmzScZ%WNnEp&RP(8)hzwJln()m&@E*|U@rt6VsVwP5pcGHfnL9K^`w3rsM z)_SP5KF?|SO=!B%ew?wU^&b|O(UdMMxYSBYyJ!je2X}aEd|Rt zuBLOc?$6?!Imte85z~`6_6AhbK(%eW{R(G|#gp-a_I}PTDS)MCgO$r*MF#*lhv7Jf z!5d);$2kli0^|TVhvCD45dg_v4qO3@1V#Z@0apW~fib|fKpt=%kZ&*1S#rOL$Ra%D z#Q;K6UIHLA<&%LM0feQz0+<3!1*QWtfSCZoQa%gt*$ht;n%sr#Jm7BN9ssc@pAXy* zJOC^JEW5kG(tHR#IYLg3kdrS3o&=TwPXSK@&j8DTXMyLijcl%QNW<34ZN%7#nWYF) zzQse^2-Z3l0n8`XhmUTesl5S1R_hR$R|z4j>7wX5t}6&oXD<=1>zcY1)crYJ*R`Ve zTCVHsHLmNrz%H{v>}(sQ>$*w0uA9Y$*MQy1o^3U3UoA^=;w0 zz9U`Noi(njwPlNGUANT}&399ewVl^J!g<~6bYAxboYxPW&g+Mw@dY}sACbdqXGO9I z_OaOU_S6255&eniMC=N16>VQ6OStA!QAWE0n!-Mw8NY)I`83Uf7j8VHu z6GEQhfR3L9hp%zwJ{Lvgm71F08VL1jp2qVd zfDOP#U=y$z*a{$^YSJ*pKt3rJgFo^ zLb?IJMm*ux!|W9;5FEPfdR{h+MUW$t5rq{}+mA_LZW3r`f8xc?7A^949?tFT80P5- z4*=qFjh)AD@xO3`q2b=tcF^Y7H^;D~bnH>+BueZibZxaCfaoz|;a{7<(y>Q9gnnYJ zR=xhiSUOK?VJY86_rHe_yejv_5qX4VaaxW zEQ`zBONLOqg%pgg^#&wvkUnY~f4!;gwV#Y-W}bLb7ZzF<=p}Fppr=wNuJZ~8TuNO; z)dA%er?BxXUGVeKIPkNfm?H8IYoaZ^k=-j5{LBz%KV7lVl~zhrHLHi`FE3QHk$A8x z*g6Ch?SERS#J|?}L~x z8rgaPf-l_*Ao$W_fjA%@K=7p__|lUA1YddsAO%PT(tvcJAhpT!w!C$?*;IH08@K^8Y=Wj2;Usdl-+g?gAlF%=q z@tn(cznzxElEV6k8y+s+a8M^;O8Pp;+^>er{R3nkAjv$Cj$Ca|eNc6 zu|(eHio2S12$I*nJC)TB!ycxrRI=+q;`c+~nez-V9$a4nDrTnFTXn)tv&QZuZVq^41b zJ63^&(x>XB^_Qk)2GrEhp-4wOA^d!=ctgxgNIV5i6Gt#Kk;VPJS)*}#(r5} z)n`e<-69F+lZ3mK-ZxYA*?#vj!qji0j-3oUVy|e}-?JPe^CdFf)Y{O#hv&~sV zk2|QPa5ND@lFLZ%PaX5WSC@v%oI?Vp_6-~{c!=yVo#z{iDbjB^Kk!cNR)Y5im%Ouf_&MnRp1N~Hgtr)uSp*!$LFQ|zR+JHtLX zqx7xW1eYnz?d+!5OmDV_HifOf&CYMiGQtt~=?MJv`|SlySrWc(d;~uh0gnQUfyaO) zz~jIZz)}U#bbvMf7eI}>ai!R{UGmchV$CYLmRcbm$LU<{5KotJ;67Sgf=X7KMZC6m zO3SQ@x;hur&J_kqrkx`Wv<1`F`kD56fNAR`(>6$^ZIn#gB$>8ZGHpwcX*KCRFfg)d8sB}{`p&~EmvCDOS4%<(t7Y^ z1F#X;1Z)Pj09)<-sGfw7Pe;h7BjVE$@#*`3_kj<94}p(>{lF){r@#T=AaDpc415NB z4m#31)Z(q_@UQSbH9T4u4(|2eFdOpoBBJB(flVxMa{nTVPszpq2afJj+fPQpX_`af zQ)nZ}TFDc2=R-gJj6=je)QTmzs~sf|;{>1mWjAQe8nLr>&(^GA6GI8qt9PT*z$v{a z@C?mh_MFzNv1+LH3nJC%Q9u*bkWUfu0=8k`z`uT~0iQ-114P|eS$JnU5MLNBdqf-f ztwy+Af|NxNdrd=xd(I_v#9rHmHHv{7Sewd6?c*qCQMRWoi*4c91dV7CJvTKC53wY! zH;)SJH-Tu-@P-Z{2%`=Rhz7W2#E^}#ceiDcsW@}2r5N{+;f$F`XCXYs{=F^hm>ip% zjTd)$0`1iFs$JmpA0OJ&rmW&{+K97D+aUyxx9@Go8nXm@MLWd8iT058EY&{N4!d!E z8-L6pqe(Ii=qFBLAi(qZPvga*snLL%b;46pG7pQ=dFloyL{%AWyTSI6SmGmOTe^N@jg2p(t0@+kZJ z4lF&YNv&=YJF*hQymLFUwye3`;v$yrjjwrrV6+fd8c!x;1a@bmrTC2ruQYixsb9Qb zKY7`ip*pk>_hLQng`D_=K;K!`iI)?tTjzz*zE2OJFsHI-bV65-@LgQF?70;gqtwJe z^VXt4qL7gm$!Jq^9Bm|0w0O?b=;(`CQYQ+7JDp)k^gn&?f&b-s)jp*IjKpui@4zYG z58yO#2B@}gznJB9M?7mFo;B(N$v^`j1xN+bfOH@OXbdy~ngcC>mO!R`vJ=je@g=l0 zLCQCH(1|CT%6iPfMNnFPwn%H?V!LZ6*4WpHu7q}Tlb+YE$?~dKg2`S6YJku5`mfPB zD1t69b>expf6;UaR-!AaM4$#@puA?)bq$JilOm_Y{lcPC?ZcgzTRfeuPj5>Y7XcRo zoq*0j7oaPQ>fo9yV$qPJD>{}L^LSL#Esw;N2$uS-BBjT7H5S5ab*OeUeaCe*xU1{&>%vuiZ`^vAYY~r|e1zS<3+oco4@1`Sboy95 zS0|`Of2-B=3AD`Po1@JDlG5mI%8%W6uKigTmYPH1Ubw>kR94YPh2rcqhGx;|1M~y> z0|TH6zLNK^oaKrp<{7Vy7_e%XPMQgt&SCbuz2LbHw{PeQ`+S*QuQzMrqY=a>sUzr< zRQ!}7AZy^kPnmrvw+zEo;n#XW1{fa59CCtPtsC!;ah}mn1Zo(y&f!; z71*tMVCx!Z=k{O?dtz_GegPK+uL$X4&2_~1HE)p{TE2*!E_I~u`Dk5Q8 z?5rp2!fvp;^~96f;+nOsllhut&+ExrvJ(4PZ`RB{&=Z?yDHWxOw})u;Pqyp#V(rA| ztR~E#d_+ZMB5MaVsV!$#spmRM4$+`0lVNhVP`BNBWp%|1FDvkLter1~U#nh&(SUwNlmDpga z_EebnyR zmu2E%_zitoqux(Yjrd@qi%yeB2Hjtg8t`IBpJBcf&EyUsHa1Vn%XbWTs;$7E+@t(CI(huJAKn( zEO$ux=V_Uk0HFni7*#9{lN30eF`h+>nwbl9^IWi-ntAk3ci>`JS`~fGX5Y}CMMeE1 zD9^=|!dvan;@I={ivF0_7pVI0=aojRJ+CiPBeBWJdF>tT_?sa3^2{z6z?!54-ar>C zdc2Z^6}%957_jYK16alw9Htmcfn~r`z|+7pz;fVO;5pzQ!1KTh{@?fu26(Ps!@Qp* z^BA6j8E__H6vzY<306^rXWhCyJZiZ-zGMAR`kI|H5TQ_&eLa%Sfdi932o`vXW59Wm zei^;y5)xRgwKon#Ao03=fRc6gsevqS5Paa;TR*0W+*}vkTIt|#AQACFP|Y?AmEbr- z?U$kU8om<0H`_}G;lV5I=h%Cz*$iDw;6+Ll3MzqBz-vGiuo_qctOZ^N)&U5!@j9Yn zAjCE{<8M(sh=;Nw9**&$4-K9{dCS#&ealL|S<^P)FUDoT@Nd;LZ6yjA)4U#hpxkA9 zCErm~9W&R{R8->+*T`zr_A@*QqP1r7w{x?&$#>>v=_cQ0mkwtA*dF_}!R%7sW?eL4 zYxwp`mZu~B$9RDjPvpX1-Qk%gIyW4WMI29!1Cp60EAuM138^rLR7t4RO6Gskoj&bVUY5cU4&h~?eZJ&Xs-#$CK+IRkUI^{wf z2Vk)N@?#0DFxiDCHD4S}@{-h3k3 zp@m`cLo}kA;boF=o;XLth8~xv{`s4Jh9OOUBsUA@N}bYF+B0L+SF`0SXVx=7(f851zZ3v1zc4M5|o`m;xhF#B)BSCA6WmG z1YDKc0SWFP7}pQIr+U?u zT<{^;q4!L~6LUVj|4p?uVvbwt3$=gC^$ER6Veo+z_Q<;cK0s5bHqWbhq-qTus?9Y# zasLt3bTOs&+FX{LGT*6%SglwRG;J}oT2JOObKFB-EmvxT*LiDfs`lW2N}GR48Q&7G z_Ow(c)uGH{!{hmvriRv4=wcJA3>2c}6z3A0`mCB?! zlzIMdmcCf+82A&h^0EGD<&&KZlXem-pHze8Cmui<;sN-KEt8heBle0bSgenXFV;G- z0&9l!jWPN4NKCn&9BWct`e#jW6N-{DLBk`bX@{3T zFNbgF7=F9q@%~H0O9P=dBmTe`(j?TGCz?DAPcs9%N5q5Oqb;=-({Stdc=<7@QzM5? z?_ZEkzB*P5N(oy`H!a-M_9F@2y9?IopjU4qb;@w)^l9k!zZtf%)zJ4hDssH_0C$@- zY~s)`%3fvsBEb%5Mh9{UD@eVvUIo-UqSN}p@OH;?SWczsSAW26f z#D%@QMtkY|Hgib#2t$r8&DPZI9SU{@LLNt_~I=>>+ z1%Co+#O&}IgXAbJ9a>x+>eZKdjmxE2rbBE@h?v&LYg{SCvVvkheqG?A7I=-(QXpFh z$cw7_n~NetiI5T(l`k)xTqo1CXF_keK5kUc(;^w|DftY6Z?q6r6M5&q&MD4#k7B4~8=EcE_utHe4T~5B1kJ z#7PL%#;s$cuQK(V?yL3TRr+OX^ebDg(MQYP!u2tDMNXWZD5j9cGZvGLUZanb9WuR8 zA5Q}IucO(|(Gy>-(ys@Kfs$A4FUPRWK1Py^{v4A&PU(17`4V3^ve8WbJxTmAp_)G} zd6pjc{O4}9zfqT;-Bm_txVND1ipzQlDgjF`d7(#$ZNH7VTsFw)1p)9u0 z?xT*1iYyWdFd*QDjDQLviVh-(8?HFGhUI@x)m{1~!Jk>aXPoDy>UN!~I#qqnTc@hK zGk5OS5AM~Y-n#I&or(Y8# z4LdxW)X%N;m4ySSk%FSq?1G&6dBrYmQ2B`tiE4`>OwqCn^4waM@?n&+L>q>-r{vF^ zlT$DoJ&#}tFT_umHp+UjNgdayg8baP2<;lHXUB2%$3|&mv~f)F+>kRTr}ga8{DRiK zii>mRmqch2td}~r_f6C$MQPV+*D=M&DJjS=DRpVrGbJ*&sBlhRap`F2J{cMhA2wwC zfMHoKZ3^V%WVp1c=y)oo#HCG#-lcLTX6DRtX*ZBcMTJnVfK<;fbZdoD^&+O|=q49M zH)0Y?67x#?=j7%M67%cQO5EDjaw=w*FYIKf(eu#K+?-;}`Ayc!E)Cet)`l*1o7{@4 zc~hpLk%ew;uB>odVD9|QFE0P4OSanO4k%nQv#6+ax=Xu@DQ*m`v}ks*m?W3B)UDkv ztKSzVInO8D>T5#}b@jljG7jQS=K5{^J-7C*EPFqom9wc2%GVF9qjmTQy3d)J599fV$mPs) zX`hfmPJ=IfDh=`kdL2WK)UUX>2rl=T_1o1M>~pKhkSyPq+E-D^liJtthn#|0({r*# zTi+NxwmnGyJruf5E&pKj_^h4y57%eq6qn}b7UY$-oK*Zk%=jCz38}L>U z21rZ%ht3L%if6ibG?d9gE~kKc;xQ=5Ey$nc;^Bh!K|`n1M#pT zXxpc#ps08#oSP@$KFld9m^~BC)r0jG&C1EmFP#tZDHtxV5A_0<3i9UU72sM!v;oq| z&dn()%`cqh;wcy=8XY+_XMP`fZS;KDByWtnF}XN@+Vs-Gyb@GQlitzWajB`B8*(C= zLs%+8)8XPRtp~2jXwgb~mzY@InkjYA&yb>A!Q3PBh{5ONf!26i>$__jOlk*e>^o@G z@L{9+We>~B?lS}#7w>?UQEg0qN&e&lsCAW_XUIM~ks*RA#0FUB$0T(agsRzK#r)Fj?6mf6i4tee0^f5G zJWp+VccNc%ndVSJ*-y3`nHTrw7EE^e_%G;dnp~7{`8T?Z z^be?-OVI;A2M^9K$tA*Z@e9y;Oew#E*&(d7v%UTveH3t>QqRA-So-iLoS_6ACcM2ywYMcSr5PR zOJ*15--sAX2dQx}avf9_DQGPl_sFG#$f9Xghc{D%=+ePp(G;2lFMz^WAD7-#v{y7I z$45b%4(`IO1wrdAsllSc>?wH#SjF`W`k7N2_?by7<}6w%N?dvyR4Rn5OTPj+WN-~W z%^{E6mB{53P_82kgA85oj7ZbPvNak*FvwLLD=Vq47mARR}+(OQ7jj>(t-N1no*sKgBCk*qSZCgfZPl{Vy+*qVjPYem$%x)C;MwGuUcbdO*K}c##VogmEkqi&1_uMCN^Os^kVvH>&(m>YqwJrVCB>HSXlwVO@bepNI4T)|Kias7L>Z5si)zo%|WM2=;{v%wLMBg#B zH$$TP0-}0Sb;4s`PoWg+m?$+DIqR-P=?f_J`=3>`$XT}>r7xir>z$~3 zD6H;wl!q^)u>4SXElDf#xU*fFw5o&#m zT39Yct$zr~l-o(N zTAGRqdzapUrib4%IyBv^75FJrVYKdw3NPRDdPHNop(B)9=U6+vQdv-`Os!O|zQ_(K zdIXyoq3{U(r1jVPTScDKBa|hulhfAG`#)x1mv3J6YD@~%y;q4pua3|o^ivm={f=6= z@`bhYrDUt(ueDm8L}gL`qO!;#LsxYS_@a{SxN4pGYa)y;`l(?sy7FtE?o(TIv`%UG zXC1Zr7duJV+&ng@R+G3&Wkgz>e!6_*W*>|B8qTmEW)A1ke=h%|q7+-&DIn?<@Wyn# zLMe~kT%l~uFttwb!}Qo470Rv(<+ZGIPYj%KkKpu`BpTZlC8pNJYP;R6i&E6AP#=Yy zJ1BR*bzOOiJ){ncRMl0sm$8P;lr(g;n(BMps9~zdsIzoWEvl~U8@oo)An<_gX~t4~ zwSz4^C~DLeLZ3XSTn8EA@GyP534P0iRz0ZI3f5n0?g_T@Fjdk-$EC7k(jwjC5iP&w z@BR^~Uw2&0N~aaD6|s-1lSwpE+~-(*y^*84l>j$46xD}nogRlsThl%zfgfVNcdfC?Va`bx7!&3xP&9qCYv@o6X`7t68u z^-NHCt&g>1SBI;DRHQzQW}ZPah}y{d6{Xf(*^Ijk6)QwDw7ya^TkJEfSyJ+FmQ|r( zE_dP5R%#isMY*~dCqYjrHku)DV;^W8_2X4)%KnMdyn-S%5quhnMyu1g2E3BmV>Z|JeB0J+fv$cx(hG3M? zF<01RY^Jc`Jp6!5z@wsq_WOZ#@N5fJ{m{yMBc2_#FKR5oO1V42_cc`dNHi3UhO$g; z8g6#0>RL0vIeVyRsGD``AJh%J!O6Jb()p=W)x0iJH6S0ePP}6J}!T0WuH#7HsJDC_DaS~ z>%g1w;QXlN^{fkT#$XLNaoDty_j+Q!A=P6+AK;>5Yxnz;tOxgwjQPIW)ocFVjh(T+ ze=)^wlgT1D^!>^Hp_Q0E!(d7H{ZFggrj+Q|;9Q@zV%}hEIu#H5l(jYMOASTXzRrKpvn#1uEzK{&*8Lg>VIrmCJJnzU}-2Qxcw zZMY>_rv@&_1}=sg2vB7l-8|XA1?$9jIO7m(Y1P{w)rJZ2Izj@Pj7axts<KK9R}dvCclaKZ%{Pw(ZZv-TL%oodmI%&}tTU zQJL&q&QO@^EF9q|46Tc4Rj!Jbh$mS?WdNwF0@wm<1-1cy0}!dGI{-YPQ+HYU2a@$S zk=twCdmyR9LF5hr?*Z=vM}Q9i1bXUG01=7$F@Qi&JqCON9Jh`gXy!woDrj8&9ykO1 z1pE^?3;YcH3j7ZI0aW_ab4bqv7l4a0)j-f10}uyl_#sjb0>;i=5^$s?3D`9Wvm1+h zAa*V3#K0Qt0X92D*z5wbDO0-zj)i@b57)3}kx*PUn$~b%Yo=)}zJ?M((^}mcN(4=7 z3D)a#@9bP7w7EUnd-EUh&FODD00*22&0vt&uqS_-w~Bx&o@qntIej(^_H-I&V4 zNP6QJoTXkCNo#3TIgF&Ka+AvC~9k)Uku3>sl*Cc?w9n0lUJ_(Q!T63AxLoXsta7JL9M_Yv+53DIG)`f?`Ff zsg>cDx2jOLQb?s;8KBBk))7pZ$vRorA8x@qTirfs#<~cOT(>7u)w+WCTCi^5&StE; z;K$au%k5dEwNk0dojtW+g5 z@&<`U>nG@8fL&Hs(8GlzL=W{oNEs50GA}BZbaG{n(iScf3z$G0PzQ(y>H-NsB9LTF z{vcIPL#~N+_XkOlnaH&U+E|-DNFCn=xvoGrpgYh5=n3=!`T%`_en5YJIsndid%zi^ zzm?7yMb0=D1C{3j>-tT5P z!p$ZNH_P?A8EByfH_H=lHkHb%x!JVKbF=AGxtg2hU!I%IaJX4P6*rqH+^kTzSrNGz zn+4Yy#)^fLl~A`%Cu61lIH&=e6L7JZxkAeCVwUSz3Y!=5h?^*Oh>i}d%-RC$p08Wr zC4n8snz38p8=34@;VnMlEeqimnXF7W%5CH*v5Tm2r%T>#ojIKtvzRXXLx+2;2FIVV zK0D^M-u|W@yVLsNcq+TgiutxByW8siX`7g(VX_D_?Bkq8S|5Mgp51GmcwR(ddK&C$ znbrA3DqCS)ccLX*X?2sUxptqm?nDx^tiPXV!0r=^_WjZ&>z|B@TP4OWmvz5OuC_X# zOk%b*{QLTBweY1Yb6r8fQpG=cWv+qAxeUfoqoGAMCPlKCWo@0hT|-j!VOiOIr=t{`3Km0?yIp z3LQoYYj6+b0U(jWv(-uAIl4(TlEU*flt@xoTSJK?g%<=VtP`ZL!A}Ys{iN`sAcakW z6w3Xi@DjCDZSCDGNP%{D)k$H?<&lEe`Bfu@ZI?$1+a08^qY5d!EJ$IeAcb9m6m|%TAeQr(`7$jtRlX^Ba5Biv}7L&wm2%-;v>NpVwV+^3}cI9VQleyp9!|WZV4g&sSsOyPFi@v*y0Pp7GDas_(HJ7SC?Rm|0GuTv&9;iTJuL7?DuLFAk#P-@=;4NSuupc-8 zybZhqybB=e*A4;i0f&JPfDeJA03v?vW8fI@iK2|uK1JpPa1!{;pPoYcIq(JWr9b@& zlz<0n{-C`#;sircT#Z2jXED+8pr0t=XJOwy9S0<|`3;<&97oYFbVtIefr5H2&Qh>{ zH-Zb-uQf}`Qx{13&o0pBZzLHzUu+TZ2n9!Ek=XUJoa#+$+jwyccD+cC#4l`%}&it|#_A9(0KFq3|OT`w2HQ6U6RE zoK!jwOL$#t!`VjI;U7HPvT35|M1)_3azULt$wou|wwA%4as8Xt^glP>P-xQ!~ znrsSJ@tE!3Ildv4n^dv2MOv0aND9MV~l#!996W z7-%eP7MoW(+2HhOM;>a4GbRK^yrrW;si+{Bohpd4I-V{B#MtN*h3aHD>O6%yu{bG~ z0)a}c1Ti)Yo)mMOouTL8NpUJ#X>B$7Et$2kdi@rk@Qs4^eU-R#EEZ1$Zp7n&_;ZZT z_pPGeQs{6%IfCcLigp&jae(#<@GI~e@H>EG0qq=c9=HHp#M34Qr~n6a0Q|ut02dGm zL;-Ff2B-yi055>O5XU}WClROzBmwn-hSr7OQdu*r;qOhFF?!xFiK^B^ zlBnE=FuraWvV6=)Q{P=UZk;Mb#g?O}hhzla(>n5dV^eGs9V|kJz9Meny=e}slpt=2 zi&%q03NK!A8~#T(*3Vk@M-$fHdg+fQF$1VVfUT@A{^-sI5)Cy14e`NM8gsB5zuNw! zEe?hG5bM^;BsMfaO~V8=4Y!U~woIT0*77L?d^<>Ud=#jvLn0sTEDbsGHTI`&@Yxt^ z;kjfs)_NTOihZ8{0GCW_y!F+&F>Hd><9vO*v@-F03Y%!%f`2E8>9bxuAIGk<_MC6b zuD7#eSt5px2NQfU$V5KTsLjDdur%@jIP46o*9Eib6uRUL9M)lv4-1FtAq&l;1I>EY z{TDp-p#Ih@|DB?vTH!1M(a%(C=Yz<52>58KQ)3niIpPHW^M!<% zGP)EyP^_;T>@P@ak+=j!aFV>ij#pU=wpg4!r(@esr*h)%G(fXUg6)L{v_@HmlG{ZE zUwcP|9=Lo*u!5<*NO!}(;+tBz|K6C|=Fna8&(Rts6Ls%$)ctem3P|5g(pO2Vq_se= zdX#^O6Z{3JyVM?_v8L=Ed4g}>p}{ilwV&2lGFv8>g^x9s%9hh(hm@EV)KhSE$lz>n z!hOz5G#r7h`qs^{sl*28i^sS~tb`7jV62-HE> zw4gjn$L7iGF}s&R$6@w3gQe+sh^(Ko%M8|=))6*X843FC26P8{06l?TKyRQg&=2Sj z3;+fKgMq7oA;3^z7%&{5c18iCfop&d&A@Mw?tgNPc(DMxSZ{$)zT`sJ5uEb z#EKm8wx}W2vX&I|z9WCbxvB-d!Q(#uuH7?+)w%j$NcP`~aw*6?t2;V0HIU+96vp!Sv(~c|;M5G@G0x1Z^>>r96F@Z*iK#tNS zjoMKCZODg*fi^x0c2e#AgLI|#Id)Ze@M?Q%vqTZSu2<6GNY4Xnffs;vz zCHHvw$c`CGs~U_Wpm0sp)WyaT)o zV7JZ>0q@xl)MgD3yyC@F8N9k4fotn{)#`Cf#a9@rd_#>~`)ji9(7Or$z@^hP7vbJY z?#;@PjP$JIV_;+|UAt{-E9}ti}kGBRq!L2v{!9L|-DeR2jOMjAH`cL7d zXYtxXQgi-upnimWUm~pjFM&drjjsdZV#TIEEGV>mcO|I&Hw0Y`4fQdM@pp=5RsKiF zw^?7Cd08EMRxFEB`8f*tQdNH5ej=9DQTYY?#&i~Mza7i)r3yuSsWFUt^`DjH*vHZl z!Kp#1`u1%m7IW^naL8nFK4^tX(wK7vYgK#qAow0Uj``f*#Tsz=+q<5(l+wr^;_2F0R<@Gk#ucdNq^ zSuF}36YQco%oT&ME*OXGv)v8ZlDdckFf4Igs{AgJe!KUNUu8C`;u8yTwQ9& z|1OdYHS`2Kr!GQ`L~)*%{f(k1nM%w#RvQGc_8c37kfTx=P^k>T!|MKjDCPL~oH9sU zJyyvQDwRp%ZKMjN6+H1P;A`MCfR^~Tz<0p+zz@I~`%qn$+5mf8UJ0B7&I8!z!dVp^ z`&?bM;}ck_F9z9KKy3h9Sv?lOc2>s@R>ziB$Cg&dmR1KR=!sa>@g}<8^=J8AAJMtq z48sk&zTTV$AHJ#|s=BJ{;#?ut{-rgG55B+zlF(aHMOEO)Q3Cu6CGYA zh4ing_N6IgfN1xU6f%&C9TbAM<@CXlG_J1NiJvrvI7nltsNPlV_57qUOmuh&(il#K z)yje{9z6ckN01=s?-E=PB8`zGcsZmo3W6YwfUbfxMmtF38fqwY@8jz-6hM#qju$BsrH3!1=ZDv~B5dP4*^Y8Q~EE-d@xTXa;I@{+TtD2VJ#wUuu^% zVa;Riq5k}~dKRyq`0Qs}v832#RVUyVyAfVA;~3O(dJ8WlR-7g{`~Hn2>a&%Bm-HfN z`>x+7#^WLX(*7X#!#NXv1P=;yi#(%c-2FlhOT7%-5p(OS>{l|elCBo7xcSH_8Y$7| zl%L=f%g!ms#G1JSi)lyoib{60Qt2C5JfDR3DEbY+44?p*2^0cFz$^fpW*wVmy%fO4 zS)T)7^Q_MUuz}XGfz}rQw*c5Y>prsXg~*iIr<-9py$9K4z;a*(uoAcruz>r4RlsWa z2R`2N`-lG}VdHKL>^Y28evCo>L0jeUWt6jO%jjAmbcl=$j$>bF&1wagQ?j%dsJupx zT317f@Tm1Q6bO&nAU$fM^r%hJqssjr^^)|c&C;VPq(^NDc@&Nq!#&6EQQN9s_j}ae zs(aLS61{{+g*MZ1Y^U_7T_KO!Ej{WL=~1sqk9wV&3-_ou93J(i^eFK#x>LsAtEzcv zk9w=BN9~gywV#@(?on@7Z8_vo??{h&x2lBSqYj396pX;Xd|bk#-jg16SjdHW)cew- zjtGw$8Sp4p_y6lr&nUE}JO?}vtOZ^G)&c8*4ZucVlij2>ORa-VDGo6e9UD>|8&Z8Y z@QR&-qL}^2z74zsyldavnk6=4^j)KLlE}~Toem;b7oL&m-$H%)84@|kcVZEV@jt|6 zFW}Pm(lCF(qmhPVX5qq*G+?phYa@wiDAEJZI38c92NXTb8EkzuBWj`a2%4B)LNmR&{!?o7_-8M-4h$)2YvS z`8Y+tVE@sUB{kG924s!EHZ>Ti7>ana#5Bad(Mi0=?8#TK#_=lEbzVPCEMhmpf@QD2 zf<^goeBpNl?0%!cwDefqv6LB5@=}gxU|z#p7mmRrgG)_t+i8fsq*IX}X;o6|3=GN! zk6H{DF-P;@s$@h~>4v*$5V6xK?&G!W$J?9@~pp z5@=|f9auelP(Uxp+kHE*)L~*5DUQ8Oj*TKMZP?nDn;g4B&>%flHE07VkH$@&C}nDi zHBOYqQ>i?W=Sg-&2evX6LSggF8(1^maN|0u{vw(q?Pv7UHK*n_<@G*6W(La)#YZ8WOdbrW$R{j z7vNXmH{f>wduSc|XZ^hG>cq0gC*qr{dO#A;5J(16fJQ)LAQeahngC6KW(uQ^vL%lx zUnv~_!*_eIfDDXGej-Ws(8@m5iKY26X}H7*CT~OY<5kDO;l1pvaC>^2+z4)1P5Md| zQ|SM&OBfC+1?sn#g~Nr1U@IE1ytW~cj#A_|al=qw?SL<&LZ1jj(R%a_jgbMg0y2Tt zKpUVf2G(B=%-+$NCHV%Ld@$}mz7~d(=nsRV-=s47sq*D8AHn4J#cT?0wfMf+=uus2 zjLAo%4Ivu4Hz+x{y5zO?bKP;;lx5HB!dedP<`=C9er<}e#M2UDTnTgpdH{ogEQ}-g zt(Eel+^r}tL+eiHCEesh?H{_Z&aqwSmy^7@S!_#P)#z&d_*`B3awony>d*sxc^u5J zBiw58VfLL}S;J9eq>&0cue?Ohmc&U;ELnol3BaDqz@E$K237EhzTZ}!Q8ZyIc=B%a zMYD1$Xgd2*Cme6t#M>)+Gak{Oe#iE-AMOoK8)$Fs&66dMW-kBlLxIx$$atG_D3dIRQ06A%px z_Rqj|7F!%wCscPh>f%ogP`7sN?kuT33@0yWIJ5_rziFgn?f%_alkQ;os`j&>X3Hq4 zF1b)xM#kLgB}+`cC}3SCzawZ~W%e)KS(nypOn!H$__<*5Q`K9lF!7Y?`W`HWk_1K3#@*i9H^06q%FV?6)tt4~O~Q@di&@$vU$f`}3Z7;5|8DmpYM6u6>{n z_v@;Si}J*EF9kfT`UTSLB>c^sDP&xd1s=aIgi|BQ0Zbmoct z&5JW|1)BP~nLlB_JdpKD^%@V*wcsp1N$L~F2WK(Hu8sb2c5$XZW=*_u5S%q!^3U4C z)Qa=EoVDiRv-XHX%0FvQiNQZj^6P^U0zwyixM&LzY6HpFp1}d<4--qYJ;=A(L8_|e{z#y;X7QVA(Ie%5d zyWQ@g8CXhQ*EDStE-RTp&_waBt{oCcu3V2vk6A(-9knJZd19FqAQ53+-j=_V`o zDz^!#2!~W*RjCDD<#r+E@=N)sTTG#&G3*vwpj(sO=2eynVUM%tg;m>J>Qz=tF=y9z zRc*7CX!Aa=@|YBs8u;YES5$5EVXyLxl#6tX=fNtC^7llePkEK~QaH*X{AATO*Ljr+ zDHa_R>r&3vR&Dfs(P)KN*(s&nLFsb#l0(?e$zrDORZonge0$YS_jr|eWHYrKVy`=z zA=6-OMV|+}$`L7C+abKKYMV#A$}uVCVLq})If;j>Hrhlqdd#bwlG0vBqaRmo^b4=@ zjTDP@h@Gm^CjUUR`JGq!NeVZ?AD9Vh@QvY#39}@A)Nk_kFg6TR6&Dg#6CxtQWj_|O z$3|rsCO=NysHt8i9m`Y4QL&N7<*9e+swYNeM40@uQ5m3-&qrl=(At+sBFqf_4pNt? zwnP$X@~@FZnfx0h(I)>6iQD8qAc-;YxOnAo1cv{#pB~QI_he*O5e< zdPIMuZc~p$5@YJoNNSlJv*oF6a!i-UV{#1I<2CgdB(bLY7bK>s*G3X&>Ru#upuPRj z2t>iZ+piMiL8lw$)-w!Pcb!4cS3V{ z*%>bGL_%mtnqhdnVMEgENLviu2@TVQrwcSqhN;&@f;&Myz;ftb<4z|i8j^tNN{UX?aa|Q zrl8SyP4xnjSW{Dwz;@oI+DqP|0mMTV!&f1*VVO5l%Z%)JIScQ?x5X*5k9IZa+UO|f#COgT+) zfoWRd)doog8#okP_U|f4^Hcps+Ow{~t@~G1d(f*rBvotZP|Y?xo_}fX$ePH+$Q^>J z3FCsVq~c=G#?(Ie1=4DQ1f)fCC|w0N@>#ETS}K$5Q0AF1gRg2%uyJ$@tB1WG8`VM> zJn>?LSr)cUVYm+(bsNXmqmMH1WVUjDf3zLBH*SE~|RAg=s#LeN``^3mNAOHH*2qFF=t zG&3N!G9q%<$XfJK$IWU_*b(oEk_KhNK<;6P>#=4(pP1s{MkCpi_A# z&wBOoockzRNOP!ou9}MfryBZ1&{4CBT=(h^$VqGx(vWM0Coy~y9U9V7_F{WymUhFjn zOR;o^*tJ!}v~FG_ONwQL#C-hv;GkA|jgeBIl@O4F+WIGhBBI2u5Q7>k2bCFWna{h_ zn5N54T9d3#ZdCY!D%xW6YLrO1HjXwIR&7%#!0+%HazE^8>kwYzF+8=yELCqs-mbSM zTGX+>iZFdL;1k;bTx$oDfvI*t0#m&T2~4#U5>QYVBw*!kNDwGd1dTu^ z##3@W+Bw>;z~Ke{WAsO<<$;Cb!SMy2J}8KO-@@?*{)^N{w&qK~W4Cv4TPZ@E>UB3p~p^K_g()1TE-Od*YT*G)EYt6m@*Wcp0J4@8e9)?UYc zve#v^oxTeF-HjFcA>c4@WFsR{Mt?$3pQ3cQsGPz_qHH3Qe}z)ff`5?mB0a$Q&r5v% zzi;_pYVKcd&G%m)(Z8mGPe1wJpVWUr1s`tW|563t|D9=F)-3q`3pRXN9bKXy-+#xA z{%sXtfB!4j6P_pS?|-KP{;IyDw*Qd|9Ub0}qj0>#fqy@x_c)XWil!e06@N5H|5*PA PWd`fV^y8E%(LeovGryK_ diff --git a/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl.py b/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl.py index 4b9fb302..75c3e99f 100644 --- a/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl.py +++ b/meteoinfo-lab/pylib/mipylib/plotlib/_axes3dgl.py @@ -653,9 +653,19 @@ class Axes3DGL(Axes3D): layer.setLegendScheme(ls) graphics = GraphicFactory.createGraphicsFromLayer(layer, offset, xshift) else: - interpolation = kwargs.pop('interpolation', None) - graphics = JOGLUtil.createTexture(layer, offset, xshift, interpolation) - #graphics = GraphicFactory.createImage(layer, offset, xshift, interpolation) + #interpolation = kwargs.pop('interpolation', None) + #graphics = GraphicFactory.createTexture(layer, offset, xshift, interpolation) + nlat = kwargs.pop('nlat', 180) + nlon = kwargs.pop('nlon', 360) + if self._axes.getProjInfo() is None: + graphics = GraphicFactory.geoSurface(layer, offset, xshift, nlon, nlat) + else: + limits = kwargs.pop('limits', None) + if limits is None: + graphics = GraphicFactory.geoSurface(layer, offset, xshift, nlon, nlat, self._axes.getProjInfo()) + else: + graphics = GraphicFactory.geoSurface(layer, offset, xshift, nlon, nlat, self._axes.getProjInfo(), + limits) lighting = kwargs.pop('lighting', None) if not lighting is None: @@ -663,7 +673,7 @@ class Axes3DGL(Axes3D): visible = kwargs.pop('visible', True) if visible: - projection = kwargs.pop('projection', migeo.projinfo()) + projection = kwargs.pop('projection', layer.getProjInfo()) self.add_graphic(graphics, projection) return graphics @@ -1609,7 +1619,7 @@ class MapAxes3D(Axes3DGL): @property def axestype(self): - return '3d_Map' + return '3d' @property def projection(self): @@ -1650,7 +1660,7 @@ class EarthAxes3D(Axes3DGL): @property def axestype(self): - return '3d_Earth' + return '3d' def earth_image(self, image): """ diff --git a/meteoinfo-projection/src/main/java/org/meteoinfo/projection/info/LambertConformalConic.java b/meteoinfo-projection/src/main/java/org/meteoinfo/projection/info/LambertConformalConic.java index 3ce8bf93..96d36153 100644 --- a/meteoinfo-projection/src/main/java/org/meteoinfo/projection/info/LambertConformalConic.java +++ b/meteoinfo-projection/src/main/java/org/meteoinfo/projection/info/LambertConformalConic.java @@ -36,7 +36,7 @@ public class LambertConformalConic extends ProjectionInfo { /** * Construction * - * @param crs Coorinate reference system + * @param crs Coordinate reference system */ public LambertConformalConic(CoordinateReferenceSystem crs) { this.crs = crs;