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 y
*/
@ -46,6 +47,22 @@ public class PointD implements Cloneable{
// </editor-fold>
// <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
* @param p PointD

View File

@ -137,6 +137,15 @@ public class ColorUtil {
// </editor-fold>
// <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
*

View File

@ -67,7 +67,7 @@ import java.util.zip.ZipInputStream;
public static String getVersion(){
String version = GlobalUtil.class.getPackage().getImplementationVersion();
if (version == null || version.equals("")) {
version = "3.7.8";
version = "3.7.9";
}
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) {
if (this.getShapeNum() == 1) {
this.setExtent((Extent) aShape.getExtent().clone());

View File

@ -37,6 +37,16 @@
<artifactId>jts-core</artifactId>
<version>1.19.0</version>
</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>
<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
* @return Double array
*/
@Override
public double[] toArray() {
return new double[]{X, Y, Z};
}
@ -102,6 +103,7 @@ public class PointZ extends PointD implements Cloneable{
* To float array
* @return Float array
*/
@Override
public float[] toFloatArray() {
return new float[]{(float) X, (float) Y, (float) Z};
}

View File

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

View File

@ -77,19 +77,6 @@ public class Polygon {
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
* @param idx Index

View File

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

View File

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

View File

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

View File

@ -1,32 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<MeteoInfo File="milconfig.xml" Type="configurefile">
<Path OpenPath="D:\Working\MIScript\Jython\mis\meteo\wrf">
<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"/>
<Path OpenPath="D:\Working\MIScript\Jython\mis\io\json">
<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\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>
<File>
<OpenedFiles>
<OpenedFile File="D:\Working\MIScript\Jython\mis\plot_types\wind\streamplot_cdata_map_1.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\meteo\wrf\wrf_destagger_uv_webmap.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\json\test_geojson_file_read_write.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\traj\hy_conc_geojson.py"/>
<OpenedFile File="D:\Working\MIScript\Jython\mis\io\json\hy_conc_nuclear_geojson.py"/>
</OpenedFiles>
<RecentFiles>
<RecentFile File="D:\Working\MIScript\Jython\mis\plot_types\wind\streamplot_cdata_map_1.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\meteo\wrf\wrf_destagger_uv_webmap.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\io\json\test_geojson_file_read_write.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\traj\hy_conc_geojson.py"/>
<RecentFile File="D:\Working\MIScript\Jython\mis\io\json\hy_conc_nuclear_geojson.py"/>
</RecentFiles>
</File>
<Font>
@ -34,5 +34,5 @@
</Font>
<LookFeel DockWindowDecorated="true" LafDecorated="true" Name="FlatDarkLaf"/>
<Figure DoubleBuffering="true"/>
<Startup MainFormLocation="-7,0" MainFormSize="1383,794"/>
<Startup MainFormLocation="-7,0" MainFormSize="1330,805"/>
</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.geo.analysis import GeometryUtil
from org.meteoinfo.geo.util import GeoProjectionUtil
from org.meteoinfo.geo.io import GeoJSONReader, GeoJSONWriter
class MILayer(object):
@ -277,6 +278,14 @@ class MILayer(object):
for shape, field in zip(shapes, fields):
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):
"""
Copy the layer.
@ -547,7 +556,7 @@ class MILayer(object):
else:
self._layer.saveFile(fn, encoding)
def savekml(self, fn):
def save_kml(self, fn):
"""
Save layer as KML file.
@ -555,7 +564,7 @@ class MILayer(object):
"""
self._layer.saveAsKMLFile(fn)
def savebil(self, fn, proj=None):
def save_bil(self, fn, proj=None):
"""
Save layer as bil file.
@ -567,6 +576,27 @@ class MILayer(object):
else:
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():
def __init__(self, data=None):

View File

@ -34,7 +34,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<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.target>8</maven.compiler.target>
<maven.compiler.release>8</maven.compiler.release>