support output GeoJSON file from Layer

This commit is contained in:
wyq 2024-01-17 10:06:32 +08:00
parent cac71057b7
commit b02bda02f5
30 changed files with 1060 additions and 38 deletions

View File

@ -33,7 +33,8 @@ public class PointD implements Cloneable{
} }
/** /**
* Contructor * Constructor
*
* @param x * @param x
* @param y * @param y
*/ */
@ -46,6 +47,22 @@ public class PointD implements Cloneable{
// </editor-fold> // </editor-fold>
// <editor-fold desc="Methods"> // <editor-fold desc="Methods">
/**
* To double array
* @return Double array
*/
public double[] toArray() {
return new double[]{X, Y};
}
/**
* To float array
* @return Float array
*/
public float[] toFloatArray() {
return new float[]{(float) X, (float) Y};
}
/** /**
* Equals of two pointDs * Equals of two pointDs
* @param p PointD * @param p PointD

View File

@ -137,6 +137,15 @@ public class ColorUtil {
// </editor-fold> // </editor-fold>
// <editor-fold desc="Methods"> // <editor-fold desc="Methods">
/**
* Convert color r,g,b to hex string
* @param color The color
* @return Hex string
*/
public static String toHex(Color color) {
return String.format("#%02X%02X%02X", color.getRed(), color.getGreen(), color.getBlue());
}
/** /**
* Convert a color to hex string * Convert a color to hex string
* *

View File

@ -67,7 +67,7 @@ import java.util.zip.ZipInputStream;
public static String getVersion(){ public static String getVersion(){
String version = GlobalUtil.class.getPackage().getImplementationVersion(); String version = GlobalUtil.class.getPackage().getImplementationVersion();
if (version == null || version.equals("")) { if (version == null || version.equals("")) {
version = "3.7.8"; version = "3.7.9";
} }
return version; return version;
} }

View File

@ -0,0 +1,77 @@
package org.meteoinfo.geo.io;
import org.meteoinfo.common.colors.ColorUtil;
import org.meteoinfo.geo.layer.VectorLayer;
import org.meteoinfo.geometry.io.geojson.*;
import org.meteoinfo.geometry.legend.ColorBreak;
import org.meteoinfo.geometry.legend.LegendScheme;
import org.meteoinfo.geometry.legend.LegendType;
import org.meteoinfo.geometry.legend.PolygonBreak;
import org.meteoinfo.geometry.shape.Shape;
import org.meteoinfo.geometry.shape.ShapeTypes;
import org.meteoinfo.ndarray.DataType;
import java.awt.*;
import java.util.Map;
public class GeoJSONReader {
/**
* Create a VectorLayer from GeoJSON feature collection
* @param features The feature collection
* @return VectorLayer object
*/
public static VectorLayer read(FeatureCollection features) {
Shape shape = GeoJSONUtil.toShape(features.getFeature(0).getGeometry());
VectorLayer layer = new VectorLayer(shape.getShapeType());
String fieldName = "title";
layer.editAddField(fieldName, DataType.STRING);
LegendScheme ls = new LegendScheme(shape.getShapeType());
ls.setLegendType(LegendType.UNIQUE_VALUE);
ls.setFieldName(fieldName);
for (int i = 0; i < features.getNumFeatures(); i++) {
Feature feature = features.getFeature(i);
Map<String, Object> properties = feature.getProperties();
String titleValue = (String) properties.get("title");
PolygonBreak cb = new PolygonBreak();
cb.setStartValue(titleValue);
cb.setCaption(titleValue);
Color color = ColorUtil.parseToColor((String) properties.get("fill"));
float alpha = Float.parseFloat(properties.get("fill-opacity").toString());
color = ColorUtil.getColor(color, alpha);
cb.setColor(color);
color = ColorUtil.parseToColor((String) properties.get("stroke"));
alpha = Float.parseFloat(properties.get("stroke-opacity").toString());
color = ColorUtil.getColor(color, alpha);
cb.setOutlineColor(color);
float lineWidth = Float.parseFloat(properties.get("stroke-width").toString());
cb.setOutlineSize(lineWidth);
ls.addLegendBreak(cb);
Geometry geometry = feature.getGeometry();
if (geometry != null) {
try {
int idx = layer.getShapeNum();
layer.editInsertShape(GeoJSONUtil.toShape(geometry), idx);
layer.editCellValue(fieldName, idx, titleValue);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
layer.setLegendScheme(ls);
return layer;
}
/**
* Create a VectorLayer from GeoJSON string
* @param json The GeoJSON string
* @return VectorLayer object
*/
public static VectorLayer read(String json) {
FeatureCollection features = (FeatureCollection) GeoJSONFactory.create(json);
return read(features);
}
}

View File

@ -0,0 +1,63 @@
package org.meteoinfo.geo.io;
import org.meteoinfo.common.colors.ColorUtil;
import org.meteoinfo.geo.layer.VectorLayer;
import org.meteoinfo.geometry.io.geojson.Feature;
import org.meteoinfo.geometry.io.geojson.FeatureCollection;
import org.meteoinfo.geometry.io.geojson.GeoJSONUtil;
import org.meteoinfo.geometry.io.geojson.Geometry;
import org.meteoinfo.geometry.legend.*;
import org.meteoinfo.geometry.shape.Shape;
import java.util.HashMap;
import java.util.Map;
public class GeoJSONWriter {
/**
* Write a vector layer to GeoJSON feature collection
*
* @param layer The vector layer
* @return GeoJSON feature collection
*/
public static FeatureCollection write(VectorLayer layer) {
Feature[] features = new Feature[layer.getShapeNum()];
LegendScheme ls = layer.getLegendScheme();
for (int i = 0; i < layer.getShapeNum(); i++) {
Shape shape = layer.getShape(i);
Geometry geometry = GeoJSONUtil.fromShape(shape);
Map<String, Object> properties = new HashMap<>();
ColorBreak cb = ls.getLegendBreak(shape.getLegendIndex());
switch (cb.getBreakType()) {
case POINT_BREAK:
PointBreak pointBreak = (PointBreak) cb;
properties.put("marker-size", "medium");
properties.put("marker-color", ColorUtil.toHex(pointBreak.getColor()));
break;
case POLYLINE_BREAK:
PolylineBreak polylineBreak = (PolylineBreak) cb;
properties.put("stroke", ColorUtil.toHex(polylineBreak.getColor()));
properties.put("stroke-opacity", polylineBreak.getColor().getAlpha() / 255.f);
properties.put("stroke-width", polylineBreak.getWidth());
break;
case POLYGON_BREAK:
PolygonBreak polygonBreak = (PolygonBreak) cb;
properties.put("fill", ColorUtil.toHex(cb.getColor()));
properties.put("fill-opacity", cb.getColor().getAlpha() / 255.f);
if (polygonBreak.isDrawOutline()) {
properties.put("stroke", ColorUtil.toHex(polygonBreak.getOutlineColor()));
properties.put("stroke-opacity", polygonBreak.getOutlineColor().getAlpha() / 255.f);
properties.put("stroke-width", polygonBreak.getOutlineSize());
} else {
properties.put("stroke-opacity", 0);
}
break;
}
properties.put("title", cb.getCaption());
features[i] = new Feature(geometry, properties);
}
return new FeatureCollection(features);
}
}

View File

@ -1412,6 +1412,17 @@ public class VectorLayer extends MapLayer {
} }
} }
/**
* Edit: Remove a shape by index
* @param idx The index
*/
public void editRemoveShape(int idx) {
if (idx >= 0 && idx < this.getShapeNum() - 1) {
this._shapeList.remove(idx);
this._attributeTable.getTable().removeRow(idx);
}
}
private void updateLayerExtent(Shape aShape) { private void updateLayerExtent(Shape aShape) {
if (this.getShapeNum() == 1) { if (this.getShapeNum() == 1) {
this.setExtent((Extent) aShape.getExtent().clone()); this.setExtent((Extent) aShape.getExtent().clone());

View File

@ -37,6 +37,16 @@
<artifactId>jts-core</artifactId> <artifactId>jts-core</artifactId>
<version>1.19.0</version> <version>1.19.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.4</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -0,0 +1,46 @@
package org.meteoinfo.geometry.io.geojson;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonPropertyOrder({"type", "id", "geometry", "properties"})
public class Feature extends GeoJSON {
@JsonInclude(Include.NON_EMPTY)
private final Object id;
private final Geometry geometry;
private final Map<String, Object> properties;
public Feature(
@JsonProperty("geometry") Geometry geometry,
@JsonProperty("properties") Map<String,Object> properties) {
this(null, geometry, properties);
}
@JsonCreator
public Feature(
@JsonProperty("id") Object id,
@JsonProperty("geometry") Geometry geometry,
@JsonProperty("properties") Map<String,Object> properties) {
super();
this.id = id;
this.geometry = geometry;
this.properties = properties;
}
public Object getId() {
return id;
}
public Geometry getGeometry() {
return geometry;
}
public Map<String, Object> getProperties() {
return properties;
}
}

View File

@ -0,0 +1,41 @@
package org.meteoinfo.geometry.io.geojson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonPropertyOrder({"type", "features"})
public class FeatureCollection extends GeoJSON {
private final Feature[] features;
@JsonCreator
public FeatureCollection(@JsonProperty("features") Feature[] features) {
super();
this.features = features;
}
/**
* Get features
* @return Features
*/
public Feature[] getFeatures() {
return features;
}
/**
* Get number of features
* @return Number of features
*/
public int getNumFeatures() {
return features.length;
}
/**
* Get a feature by index
* @param index The index
* @return The feature
*/
public Feature getFeature(int index) {
return features[index];
}
}

View File

@ -0,0 +1,58 @@
package org.meteoinfo.geometry.io.geojson;
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type"
)
@JsonSubTypes( {
@JsonSubTypes.Type(value=Point.class, name="Point" ),
@JsonSubTypes.Type(value=LineString.class, name="LineString" ),
@JsonSubTypes.Type(value=Polygon.class, name="Polygon" ),
@JsonSubTypes.Type(value=MultiPoint.class, name="MultiPoint" ),
@JsonSubTypes.Type(value=MultiLineString.class, name="MultiLineString" ),
@JsonSubTypes.Type(value=MultiPolygon.class, name="MultiPolygon" ),
@JsonSubTypes.Type(value=Feature.class, name="Feature" ),
@JsonSubTypes.Type(value=FeatureCollection.class, name="FeatureCollection" ),
@JsonSubTypes.Type(value=GeometryCollection.class, name="GeometryCollection" )
})
public abstract class GeoJSON {
private static final ObjectMapper mapper = new ObjectMapper();
@JsonProperty("type")
private String type;
@JsonCreator
public GeoJSON() {
setType(getClass().getSimpleName());
}
public String toString() {
try {
return mapper.writeValueAsString(this);
} catch (JsonGenerationException e) {
return "Unhandled exception occured when serializing this instance";
} catch (JsonMappingException e) {
return "Unhandled exception occured when serializing this instance";
} catch (IOException e) {
return "Unhandled exception occured when serializing this instance";
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}

View File

@ -0,0 +1,65 @@
package org.meteoinfo.geometry.io.geojson;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.MapType;
public class GeoJSONFactory {
private static final ObjectMapper mapper = new ObjectMapper();
public static GeoJSON create(String json) {
try {
JsonNode node = mapper.readTree(json);
String type = node.get("type").asText();
if (type.equals("FeatureCollection"))
return readFeatureCollection(node);
else if (type.equals("Feature"))
return readFeature(node);
else
return readGeometry(node, type);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static FeatureCollection readFeatureCollection(JsonNode node)
throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
Iterator<JsonNode> it = node.get("features").iterator();
List<Feature> features = new ArrayList<>();
while (it.hasNext())
features.add(readFeature(it.next()));
return new FeatureCollection(features.toArray(new Feature[features.size()]));
}
private static Feature readFeature(JsonNode node)
throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
JsonNode geometryNode = node.get("geometry");
MapType javaType = mapper.getTypeFactory().constructMapType(Map.class, String.class, Object.class);
JsonNode id = node.get("id");
Map<String, Object> properties = mapper.readValue(node.get("properties").traverse(), javaType);
Geometry geometry = readGeometry(geometryNode);
return new Feature(id, geometry, properties);
}
private static Geometry readGeometry(JsonNode node)
throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
if (node != null && !node.isNull())
return readGeometry(node, node.get("type").asText());
else
return null;
}
private static Geometry readGeometry(JsonNode node, String type)
throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
return (Geometry) mapper.readValue(node.traverse(), Class.forName("org.meteoinfo.geometry.io.geojson." + type));
}
}

View File

@ -0,0 +1,341 @@
package org.meteoinfo.geometry.io.geojson;
import org.meteoinfo.common.PointD;
import org.meteoinfo.geometry.shape.*;
import java.util.ArrayList;
import java.util.List;
public class GeoJSONUtil {
/**
* Convert GeoJSON geometry to shape
*
* @param geometry The geometry
* @return Shape
*/
public static Shape toShape(Geometry geometry) {
if (geometry instanceof Point) {
return toShape((Point) geometry);
} else if (geometry instanceof LineString) {
return toShape((LineString) geometry);
} else if (geometry instanceof MultiLineString) {
return toShape((MultiLineString) geometry);
} else if (geometry instanceof Polygon) {
return toShape((Polygon) geometry);
} else if (geometry instanceof MultiPolygon) {
return toShape((MultiPolygon) geometry);
} else {
throw new UnsupportedOperationException();
}
}
/**
* Convert shape to GeoJSON geometry
*
* @param shape The shape
* @return Geometry
*/
public static Geometry fromShape(Shape shape) {
if (shape instanceof PointShape) {
return fromShape((PointShape) shape);
} else if (shape instanceof PolylineShape) {
return fromShape((PolylineShape) shape);
} else if (shape instanceof PolygonShape) {
return fromShape((PolygonShape) shape);
} else {
throw new UnsupportedOperationException();
}
}
/**
* Convert PointShape to GeoJSON Point
*
* @param pointShape The PointShape object
* @return GeoJSON Point object
*/
public static Point fromShape(PointShape pointShape) {
PointD p = pointShape.getPoint();
return new Point(p.toArray());
}
/**
* Convert GeoJSON Point to PointShape
*
* @param point The GeoJSON Point object
* @return PointShape object
*/
public static PointShape toShape(Point point) {
double[] coordinates = point.getCoordinates();
if (coordinates.length == 2) {
return new PointShape(new PointD(coordinates[0], coordinates[1]));
} else {
return new PointZShape(new PointZ(coordinates[0], coordinates[1], coordinates[2]));
}
}
/**
* Convert PolylineShape to GeoJSON LineString or MultiLineString
*
* @param polylineShape The PolylineShape object
* @return GeoJSON LineString or MultiLineString object
*/
public static Geometry fromShape(PolylineShape polylineShape) {
if (polylineShape.isMultiLine()) {
int lineNum = polylineShape.getPartNum();
double[][][] coordinates = new double[lineNum][][];
int cNum = polylineShape.getShapeType() == ShapeTypes.POLYLINE ? 2 : 3;
for (int j = 0; j < lineNum; j++) {
Polyline polyline = polylineShape.getPolylines().get(j);
int pNum = polyline.getPointList().size();
double[][] matrix = new double[pNum][cNum];
for (int i = 0; i < pNum; i++) {
matrix[i] = polyline.getPointList().get(i).toArray();
}
coordinates[j] = matrix;
}
return new MultiLineString(coordinates);
} else {
int cNum = polylineShape.getShapeType() == ShapeTypes.POLYLINE ? 2 : 3;
double[][] coordinates = new double[polylineShape.getPointNum()][cNum];
for (int i = 0; i < polylineShape.getPointNum(); i++) {
coordinates[i] = polylineShape.getPoints().get(i).toArray();
}
return new LineString(coordinates);
}
}
/**
* Convert GeoJSON LineString to PolylineShape
*
* @param lineString The GeoJSON LineString object
* @return PolylineShape object
*/
public static PolylineShape toShape(LineString lineString) {
double[][] coordinates = lineString.getCoordinates();
int pNum = coordinates.length;
if (coordinates[0].length == 2) {
List<PointD> points = new ArrayList<>();
for (int i = 0; i < pNum; i++) {
points.add(new PointD(coordinates[i][0], coordinates[i][1]));
}
return new PolylineShape(points);
} else {
List<PointZ> points = new ArrayList<>();
for (int i = 0; i < pNum; i++) {
points.add(new PointZ(coordinates[i][0], coordinates[i][1], coordinates[i][2]));
}
return new PolylineZShape(points);
}
}
/**
* Convert GeoJSON LineString to PolylineShape
*
* @param lineString The GeoJSON LineString object
* @return PolylineShape object
*/
public static PolylineShape toShape(MultiLineString lineString) {
double[][][] coordinates = lineString.getCoordinates();
int lineNum = coordinates.length;
int cNum = coordinates[0][0].length;
if (cNum == 2) {
PolylineShape polylineShape = new PolylineShape();
polylineShape.setPartNum(lineNum);
polylineShape.parts = new int[lineNum];
List<PointD> points = new ArrayList<>();
for (int j = 0; j < lineNum; j++) {
int pNum = coordinates[j].length;
polylineShape.parts[j] = pNum;
for (int i = 0; i < pNum; i++) {
points.add(new PointD(coordinates[j][i][0], coordinates[j][i][1]));
}
}
polylineShape.setPoints(points);
return polylineShape;
} else {
PolylineZShape polylineZShape = new PolylineZShape();
polylineZShape.setPartNum(lineNum);
polylineZShape.parts = new int[lineNum];
List<PointZ> points = new ArrayList<>();
for (int j = 0; j < lineNum; j++) {
int pNum = coordinates[j].length;
polylineZShape.parts[j] = pNum;
for (int i = 0; i < pNum; i++) {
points.add(new PointZ(coordinates[j][i][0], coordinates[j][i][1], coordinates[j][i][2]));
}
}
polylineZShape.setPoints(points);
return polylineZShape;
}
}
/**
* Convert PolygonShape to GeoJSON Polygon or MultiPolygon
*
* @param polygonShape The PolygonShape object
* @return GeoJSON Polygon or MultiPolygon object
*/
public static Geometry fromShape(PolygonShape polygonShape) {
int cNum = polygonShape.getShapeType() == ShapeTypes.POLYGON ? 2 : 3;
if (polygonShape.isMultiPolygon()) {
int polygonNum = polygonShape.getPolygons().size();
double[][][][] coordinates = new double[polygonNum][][][];
for (int k = 0; k < polygonNum; k++) {
org.meteoinfo.geometry.shape.Polygon sPolygon = polygonShape.getPolygon(k);
int ringNumber = sPolygon.getRingNumber();
double[][][] a3d = new double[ringNumber][][];
int pNum = sPolygon.getOutLine().size();
double[][] matrix = new double[pNum][cNum];
for (int i = 0; i < pNum; i++) {
matrix[i] = sPolygon.getOutLine().get(i).toArray();
}
a3d[0] = matrix;
if (sPolygon.hasHole()) {
for (int j = 0; j < sPolygon.getHoleLineNumber(); j++) {
pNum = sPolygon.getHoleLine(j).size();
matrix = new double[pNum][cNum];
for (int i = 0; i < sPolygon.getHoleLine(j).size(); i++) {
matrix[i] = sPolygon.getHoleLine(j).get(i).toArray();
}
a3d[j + 1] = matrix;
}
}
coordinates[k] = a3d;
}
return new MultiPolygon(coordinates);
} else {
org.meteoinfo.geometry.shape.Polygon sPolygon = polygonShape.getPolygon(0);
int ringNum = sPolygon.getRingNumber();
double[][][] coordinates = new double[ringNum][][];
int pNum = sPolygon.getOutLine().size();
double[][] matrix = new double[pNum][cNum];
for (int i = 0; i < pNum; i++) {
matrix[i] = sPolygon.getOutLine().get(i).toArray();
}
coordinates[0] = matrix;
if (sPolygon.hasHole()) {
for (int j = 0; j < sPolygon.getHoleLineNumber(); j++) {
pNum = sPolygon.getHoleLine(j).size();
matrix = new double[pNum][cNum];
for (int i = 0; i < sPolygon.getHoleLine(j).size(); i++) {
matrix[i] = sPolygon.getHoleLine(j).get(i).toArray();
}
coordinates[j + 1] = matrix;
}
}
return new Polygon(coordinates);
}
}
/**
* Convert GeoJSON Polygon to PolygonShape
*
* @param polygon The GeoJSON Polygon object
* @return PolygonShape object
*/
public static PolygonShape toShape(Polygon polygon) {
double[][][] coordinates = polygon.getCoordinates();
int ringNum = coordinates.length;
int cNum = coordinates[0][0].length;
if (cNum == 2) {
PolygonShape polygonShape = new PolygonShape();
polygonShape.setPartNum(ringNum);
polygonShape.parts = new int[ringNum];
List<PointD> points = new ArrayList<>();
for (int j = 0; j < ringNum; j++) {
int pNum = coordinates[j].length;
polygonShape.parts[j] = pNum;
for (int i = 0; i < pNum; i++) {
points.add(new PointD(coordinates[j][i][0], coordinates[j][i][1]));
}
}
polygonShape.setPoints(points);
return polygonShape;
} else {
PolygonZShape polygonZShape = new PolygonZShape();
polygonZShape.setPartNum(ringNum);
polygonZShape.parts = new int[ringNum];
List<PointZ> points = new ArrayList<>();
for (int j = 0; j < ringNum; j++) {
int pNum = coordinates[j].length;
polygonZShape.parts[j] = pNum;
for (int i = 0; i < pNum; i++) {
points.add(new PointZ(coordinates[j][i][0], coordinates[j][i][1], coordinates[j][i][2]));
}
}
polygonZShape.setPoints(points);
return polygonZShape;
}
}
/**
* Convert GeoJSON MultiPolygon to PolygonShape
*
* @param multiPolygon The GeoJSON MultiPolygon object
* @return PolygonShape object
*/
public static PolygonShape toShape(MultiPolygon multiPolygon) {
double[][][][] coordinates = multiPolygon.getCoordinates();
int polygonNum = coordinates.length;
int cNum = coordinates[0][0][0].length;
if (cNum == 2) {
List<org.meteoinfo.geometry.shape.Polygon> polygons = new ArrayList<>();
for (int k = 0; k < polygonNum; k++) {
int ringNum = coordinates[k].length;
org.meteoinfo.geometry.shape.Polygon polygon = new org.meteoinfo.geometry.shape.Polygon();
for (int j = 0; j < ringNum; j++) {
List<PointD> points = new ArrayList<>();
int pNum = coordinates[k][j].length;
for (int i = 0; i < pNum; i++) {
points.add(new PointD(coordinates[k][j][i][0], coordinates[k][j][i][1]));
}
if (j == 0) {
polygon.setOutLine(points);
} else {
polygon.addHole(points);
}
}
polygons.add(polygon);
}
PolygonShape polygonShape = new PolygonShape();
polygonShape.setPolygons(polygons);
return polygonShape;
} else {
List<org.meteoinfo.geometry.shape.Polygon> polygons = new ArrayList<>();
for (int k = 0; k < polygonNum; k++) {
int ringNum = coordinates[k].length;
org.meteoinfo.geometry.shape.Polygon polygon = new org.meteoinfo.geometry.shape.Polygon();
for (int j = 0; j < ringNum; j++) {
List<PointZ> points = new ArrayList<>();
int pNum = coordinates[k][j].length;
for (int i = 0; i < pNum; i++) {
points.add(new PointZ(coordinates[k][j][i][0], coordinates[k][j][i][1],
coordinates[k][j][i][2]));
}
if (j == 0) {
polygon.setOutLine(points);
} else {
polygon.addHole(points);
}
}
polygons.add(polygon);
}
PolygonZShape polygonZShape = new PolygonZShape();
polygonZShape.setPolygons(polygons);
return polygonZShape;
}
}
}

View File

@ -0,0 +1,33 @@
package org.meteoinfo.geometry.io.geojson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type"
)
@JsonSubTypes( {
@JsonSubTypes.Type(value=Point.class, name="Point" ),
@JsonSubTypes.Type(value=LineString.class, name="LineString" ),
@JsonSubTypes.Type(value=Polygon.class, name="Polygon" ),
@JsonSubTypes.Type(value=MultiPoint.class, name="MultiPoint" ),
@JsonSubTypes.Type(value=MultiLineString.class, name="MultiLineString" ),
@JsonSubTypes.Type(value=MultiPolygon.class, name="MultiPolygon" ),
@JsonSubTypes.Type(value=Feature.class, name="Feature" ),
@JsonSubTypes.Type(value=FeatureCollection.class, name="FeatureCollection" ),
@JsonSubTypes.Type(value=GeometryCollection.class, name="GeometryCollection" )
} )
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({"type", "coordinates", "bbox"})
public abstract class Geometry extends GeoJSON {
@JsonCreator
public Geometry() {
super();
}
}

View File

@ -0,0 +1,20 @@
package org.meteoinfo.geometry.io.geojson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonPropertyOrder({"type", "geometries"})
public class GeometryCollection extends Geometry {
private final Geometry[] geometries;
@JsonCreator
public GeometryCollection(@JsonProperty("geometries") Geometry[] geometries) {
super();
this.geometries = geometries;
}
public Geometry[] getGeometries() {
return geometries;
}
}

View File

@ -0,0 +1,27 @@
package org.meteoinfo.geometry.io.geojson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(Include.NON_NULL)
public class LineString extends Geometry {
private final double[][] coordinates;
private final double[] bbox;
@JsonCreator
public LineString(@JsonProperty("coordinates") double [][] coordinates) {
super();
this.coordinates = coordinates;
this.bbox = null;
}
public double[][] getCoordinates() {
return coordinates;
}
public double[] getBbox() {
return bbox;
}
}

View File

@ -0,0 +1,27 @@
package org.meteoinfo.geometry.io.geojson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(Include.NON_NULL)
public class MultiLineString extends Geometry {
private final double[][][] coordinates;
private final double[] bbox;
@JsonCreator
public MultiLineString(@JsonProperty("coordinates") double [][][] coordinates) {
super();
this.coordinates = coordinates;
this.bbox = null;
}
public double[][][] getCoordinates() {
return coordinates;
}
public double[] getBbox() {
return bbox;
}
}

View File

@ -0,0 +1,27 @@
package org.meteoinfo.geometry.io.geojson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(Include.NON_NULL)
public class MultiPoint extends Geometry {
private final double[][] coordinates;
private final double[] bbox;
@JsonCreator
public MultiPoint(@JsonProperty("coordinates") double [][] coordinates) {
super();
this.coordinates = coordinates;
this.bbox = null;
}
public double[][] getCoordinates() {
return coordinates;
}
public double[] getBbox() {
return bbox;
}
}

View File

@ -0,0 +1,27 @@
package org.meteoinfo.geometry.io.geojson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(Include.NON_NULL)
public class MultiPolygon extends Geometry {
private final double[][][][] coordinates;
private final double[] bbox;
@JsonCreator
public MultiPolygon(@JsonProperty("coordinates") double [][][][] coordinates) {
super();
this.coordinates = coordinates;
this.bbox = null;
}
public double[][][][] getCoordinates() {
return coordinates;
}
public double[] getBbox() {
return bbox;
}
}

View File

@ -0,0 +1,27 @@
package org.meteoinfo.geometry.io.geojson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(Include.NON_NULL)
public class Point extends Geometry {
private final double[] coordinates;
private final double[] bbox;
@JsonCreator
public Point(@JsonProperty("coordinates") double [] coordinates) {
super();
this.coordinates = coordinates;
this.bbox = null;
}
public double[] getCoordinates() {
return coordinates;
}
public double[] getBbox() {
return bbox;
}
}

View File

@ -0,0 +1,27 @@
package org.meteoinfo.geometry.io.geojson;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(Include.NON_NULL)
public class Polygon extends Geometry {
private final double[][][] coordinates;
private final double[] bbox;
@JsonCreator
public Polygon(@JsonProperty("coordinates") double [][][] coordinates) {
super();
this.coordinates = coordinates;
this.bbox = null;
}
public double[][][] getCoordinates() {
return coordinates;
}
public double[] getBbox() {
return bbox;
}
}

View File

@ -94,6 +94,7 @@ public class PointZ extends PointD implements Cloneable{
* To double array * To double array
* @return Double array * @return Double array
*/ */
@Override
public double[] toArray() { public double[] toArray() {
return new double[]{X, Y, Z}; return new double[]{X, Y, Z};
} }
@ -102,6 +103,7 @@ public class PointZ extends PointD implements Cloneable{
* To float array * To float array
* @return Float array * @return Float array
*/ */
@Override
public float[] toFloatArray() { public float[] toFloatArray() {
return new float[]{(float) X, (float) Y, (float) Z}; return new float[]{(float) X, (float) Y, (float) Z};
} }

View File

@ -38,6 +38,15 @@ public class PointZShape extends PointShape {
this.point = new PointZ(); this.point = new PointZ();
this.updateExtent((PointZ)this.point); this.updateExtent((PointZ)this.point);
} }
/**
* Constructor
* @param pointZ The PointZ object
*/
public PointZShape(PointZ pointZ) {
this.point = pointZ;
this.updateExtent(pointZ);
}
/** /**
* Constructor * Constructor

View File

@ -77,19 +77,6 @@ public class Polygon {
return this._holeLines; return this._holeLines;
} }
/**
* Get hole lines
*
* @return hole lines
*/
public List<List<? extends PointD>> getHoleLines_bak() {
List<List<? extends PointD>> hlines = new ArrayList<>();
for (List<? extends PointD> hline : _holeLines){
hlines.add((List<PointD>)hline);
}
return hlines;
}
/** /**
* Get a hole line * Get a hole line
* @param idx Index * @param idx Index

View File

@ -150,6 +150,14 @@ public class PolygonShape extends Shape implements Cloneable {
return ShapeTypes.POLYGON; return ShapeTypes.POLYGON;
} }
/**
* Get is multi polygon or not
* @return Multi polygon or not
*/
public boolean isMultiPolygon() {
return this._numParts > 1;
}
/** /**
* To geometry method * To geometry method
* *
@ -279,6 +287,15 @@ public class PolygonShape extends Shape implements Cloneable {
return _polygons; return _polygons;
} }
/**
* Get a polygon
* @param index The polygon index
* @return The polygon
*/
public Polygon getPolygon(int index) {
return _polygons.get(index);
}
/** /**
* Set polygons * Set polygons
* *

View File

@ -58,6 +58,14 @@ public class PolylineShape extends Shape implements Cloneable {
_polylines = new ArrayList<>(); _polylines = new ArrayList<>();
} }
/**
* Constructor
* @param points Point list
*/
public PolylineShape(List<PointD> points) {
this.setPoints(points);
}
/** /**
* Constructor * Constructor
* *
@ -109,6 +117,14 @@ public class PolylineShape extends Shape implements Cloneable {
return ShapeTypes.POLYLINE; return ShapeTypes.POLYLINE;
} }
/**
* Get is multi line or not
* @return Multi line or not
*/
public boolean isMultiLine() {
return this._numParts > 1;
}
/** /**
* To geometry method * To geometry method
* *

View File

@ -41,6 +41,14 @@ public class PolylineZShape extends PolylineShape {
public PolylineZShape() { public PolylineZShape() {
super(); super();
} }
/**
* Constructor
* @param points Points
*/
public PolylineZShape(List<PointZ> points) {
this.setPoints(points);
}
/** /**
* Constructor * Constructor

View File

@ -1,32 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MeteoInfo File="milconfig.xml" Type="configurefile"> <MeteoInfo File="milconfig.xml" Type="configurefile">
<Path OpenPath="D:\Working\MIScript\Jython\mis\meteo\wrf"> <Path OpenPath="D:\Working\MIScript\Jython\mis\io\json">
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\traj"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\jogl"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\3d_earth"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\chart"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\chart\legend"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\wind"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\map\webmap"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\netcdf"/> <RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\netcdf"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo"/> <RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo\wrf"/> <RecentFolder Folder="D:\Working\MIScript\Jython\mis\meteo\wrf"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\ascii"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\plot_types\3d\3d_earth"/>
<RecentFolder Folder="D:\Working\MIScript\Jython"/>
<RecentFolder Folder="D:\Working\MIScript"/>
<RecentFolder Folder="D:\Working\MIScript\cuace_dust"/>
<RecentFolder Folder="D:\Working\MIScript\cuace_dust\py"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\traj"/>
<RecentFolder Folder="D:\Working\MIScript\Jython\mis\io\json"/>
</Path> </Path>
<File> <File>
<OpenedFiles> <OpenedFiles>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\wind\streamplot_cdata_map_1.py"/> <OpenedFile File="D:\Working\MIScript\Jython\mis\io\json\test_geojson_file_read_write.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\3d_earth\plot_cuace_earth_isosurface_streamline.py"/> <OpenedFile File="D:\Working\MIScript\Jython\mis\traj\hy_conc_geojson.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\meteo\wrf\wrf_destagger_uv_webmap.py"/> <OpenedFile File="D:\Working\MIScript\Jython\mis\io\json\hy_conc_nuclear_geojson.py"/>
</OpenedFiles> </OpenedFiles>
<RecentFiles> <RecentFiles>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\wind\streamplot_cdata_map_1.py"/> <RecentFile File="D:\Working\MIScript\Jython\mis\io\json\test_geojson_file_read_write.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\3d\3d_earth\plot_cuace_earth_isosurface_streamline.py"/> <RecentFile File="D:\Working\MIScript\Jython\mis\traj\hy_conc_geojson.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\meteo\wrf\wrf_destagger_uv_webmap.py"/> <RecentFile File="D:\Working\MIScript\Jython\mis\io\json\hy_conc_nuclear_geojson.py"/>
</RecentFiles> </RecentFiles>
</File> </File>
<Font> <Font>
@ -34,5 +34,5 @@
</Font> </Font>
<LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/> <LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/>
<Figure DoubleBuffering="true"/> <Figure DoubleBuffering="true"/>
<Startup MainFormLocation="-7,0" MainFormSize="1383,794"/> <Startup MainFormLocation="-7,0" MainFormSize="1330,805"/>
</MeteoInfo> </MeteoInfo>

View File

@ -17,6 +17,7 @@ from org.meteoinfo.projection import ProjectionUtil, KnownCoordinateSystems
from org.meteoinfo.geometry.shape import PolygonShape, ShapeTypes from org.meteoinfo.geometry.shape import PolygonShape, ShapeTypes
from org.meteoinfo.geo.analysis import GeometryUtil from org.meteoinfo.geo.analysis import GeometryUtil
from org.meteoinfo.geo.util import GeoProjectionUtil from org.meteoinfo.geo.util import GeoProjectionUtil
from org.meteoinfo.geo.io import GeoJSONReader, GeoJSONWriter
class MILayer(object): class MILayer(object):
@ -277,6 +278,14 @@ class MILayer(object):
for shape, field in zip(shapes, fields): for shape, field in zip(shapes, fields):
self._layer.editAddShape(shape, field) self._layer.editAddShape(shape, field)
def del_shape(self, shape):
"""
Delete a shape.
:param shape: (*Shape or int*) The shape or shape index to be deleted.
"""
self._layer.editRemoveShape(shape)
def copy(self): def copy(self):
""" """
Copy the layer. Copy the layer.
@ -547,7 +556,7 @@ class MILayer(object):
else: else:
self._layer.saveFile(fn, encoding) self._layer.saveFile(fn, encoding)
def savekml(self, fn): def save_kml(self, fn):
""" """
Save layer as KML file. Save layer as KML file.
@ -555,7 +564,7 @@ class MILayer(object):
""" """
self._layer.saveAsKMLFile(fn) self._layer.saveAsKMLFile(fn)
def savebil(self, fn, proj=None): def save_bil(self, fn, proj=None):
""" """
Save layer as bil file. Save layer as bil file.
@ -567,6 +576,27 @@ class MILayer(object):
else: else:
self._layer.saveFile(fn, proj) self._layer.saveFile(fn, proj)
def save_geojson(self, fn):
"""
Save layer as GeoJSON file.
:param fn: (*str*) GeoJSON file name.
"""
features = GeoJSONWriter.write(self._layer)
with open(fn, 'w') as f:
f.write("{\n")
f.write(""""type": "FeatureCollection",\n""")
f.write(""""features": [\n""")
for i in range(features.getNumFeatures()):
feature = features.getFeature(i)
f.write(feature.toString())
if i < features.getNumFeatures() - 1:
f.write(",\n")
else:
f.write("\n")
f.write("]\n")
f.write("}")
class MIXYListData(): class MIXYListData():
def __init__(self, data=None): def __init__(self, data=None):

View File

@ -34,7 +34,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<revision>3.7.8</revision> <revision>3.7.9</revision>
<maven.compiler.source>8</maven.compiler.source> <maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target> <maven.compiler.target>8</maven.compiler.target>
<maven.compiler.release>8</maven.compiler.release> <maven.compiler.release>8</maven.compiler.release>