java导出postgis空间数据几何对象shapefile文件
项目开发中,需要java后端实现导出postgis空间数据几何对象shapefile文件,以便能直观查看数据详情。注意事项Shapefile 默认的几何字段名为 the_geom,若导出时未显式指定或字段名被修改,部分软件(如 ArcGIS、QGI)可能无法识别几何数据。
1.自定义几何对象构造器
package com.otitan.gz.bozhouly.forestry.industry.geotools;import org.geotools.geojson.geom.GeometryJSON;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;/*** <p>自定义几何对象构造器</p>* @author carter* @blob https://blog.csdn.net/qq_21480329* @date*/
public class GeometryCreator {private static GeometryCreator geometryCreator = null;private static GeometryFactory geometryFactory = new GeometryFactory();/*** 设置保留6位小数,否则GeometryJSON默认保留4位小数*/private static GeometryJSON geometryJson = new GeometryJSON(6);private GeometryCreator() {}/*** 返回本类的唯一实例* @return*/public static GeometryCreator getInstance() {if (geometryCreator == null) {return new GeometryCreator();}return geometryCreator;}/*** 1.1根据X,Y坐标构建一个几何对象: 点 【Point】* @param x* @param y* @return*/public Point createPoint(double x,double y){Coordinate coord = new Coordinate(x, y);return geometryFactory.createPoint(coord);}/*** 1.2根据几何对象的WKT描述【String】创建几何对象: 点 【Point】* @return* @throws ParseException*/public Point createPointByWKT(String PointWKT) throws ParseException {WKTReader reader = new WKTReader(geometryFactory);return (Point) reader.read(PointWKT);}/*** 1.3根据几何对象的WKT描述【String】创建几何对象:多点 【MultiPoint】* @return* @throws ParseException*/public MultiPoint createMulPointByWKT(String MPointWKT)throws ParseException{WKTReader reader = new WKTReader( geometryFactory );return (MultiPoint) reader.read(MPointWKT);}/*** 2.1根据两点 创建几何对象:线 【LineString】* @param ax 第一个点的x坐标* @param ay 第一个点的y坐标* @param bx 第二个点的x坐标* @param by 第二个点的y坐标* @return*/public LineString createLine(double ax,double ay,double bx,double by){Coordinate[] coords = new Coordinate[] {new Coordinate(ax, ay), new Coordinate(bx, by)};return geometryFactory.createLineString(coords);}/*** 2.2根据线的WKT描述创建几何对象:线 【LineString】* @param LineStringWKT* @return* @throws ParseException*/public LineString createLineByWKT(String LineStringWKT) throws ParseException{WKTReader reader = new WKTReader( geometryFactory );return (LineString) reader.read(LineStringWKT);}/*** 2.3根据点组合的线数组,创建几何对象:多线 【MultiLineString】* @param list* @return*/public MultiLineString createMLine(List<Coordinate[]> list){if(list == null){return null;}LineString[] lineStrings = new LineString[list.size()];int i = 0;for (Coordinate[] coordinates : list) {lineStrings[i] = geometryFactory.createLineString(coordinates);}return geometryFactory.createMultiLineString(lineStrings);}/*** 2.4根据几何对象的WKT描述【String】创建几何对象 : 多线【MultiLineString】* @param MLineStringWKT* @return* @throws ParseException*/public MultiLineString createMLineByWKT(String MLineStringWKT)throws ParseException{WKTReader reader = new WKTReader( geometryFactory );return (MultiLineString) reader.read(MLineStringWKT);}/*** 3.1 根据几何对象的WKT描述【String】创建几何对象:多边形 【Polygon】* @param PolygonWKT* @return* @throws ParseException*/public Polygon createPolygonByWKT(String PolygonWKT) throws ParseException{WKTReader reader = new WKTReader( geometryFactory );return (Polygon) reader.read(PolygonWKT);}/*** 3.2 根据几何对象的WKT描述【String】创建几何对象: 多多边形 【MultiPolygon】* @param MPolygonWKT* @return* @throws ParseException*/public MultiPolygon createMulPolygonByWKT(String MPolygonWKT) throws ParseException{WKTReader reader = new WKTReader( geometryFactory );return (MultiPolygon) reader.read(MPolygonWKT);}/*** 根据多边形数组 进行多多边形的创建* @param polygons* @return* @throws ParseException*/public MultiPolygon createMulPolygonByPolygon(Polygon[] polygons) throws ParseException{return geometryFactory.createMultiPolygon(polygons);}/*** 4.1 根据几何对象数组,创建几何对象集合:【GeometryCollection】* @return* @throws ParseException*/public GeometryCollection createGeoCollect(Geometry[] geoArray) throws ParseException{return geometryFactory.createGeometryCollection(geoArray);}/*** 5.1 根据圆点以及半径创建几何对象:特殊的多边形--圆 【Polygon】* @param x 圆点x坐标* @param y 圆点y坐标* @param radius 半径* @return*/public Polygon createCircle(double x, double y, final double radius){//圆上面的点个数final int sides = 32;Coordinate[] coords = new Coordinate[sides+1];for( int i = 0; i < sides; i++){double angle = ((double) i / (double) sides) * Math.PI * 2.0;double dx = Math.cos( angle ) * radius;double dy = Math.sin( angle ) * radius;coords[i] = new Coordinate( (double) x + dx, (double) y + dy );}coords[sides] = coords[0];//线性环LinearRing ring = geometryFactory.createLinearRing(coords);return geometryFactory.createPolygon(ring, null);}/*** 6.1 根据WKT创建环* @param ringWKT* @return* @throws ParseException*/public LinearRing createLinearRingByWKT(String ringWKT) throws ParseException{WKTReader reader = new WKTReader( geometryFactory );return (LinearRing) reader.read(ringWKT);}/*** 几何对象转GeoJson对象* @param geometry* @return* @throws Exception*/public static String geometryToGeoJson(Geometry geometry) throws Exception {if (geometry == null) {return null;}StringWriter writer = new StringWriter();geometryJson.write(geometry, writer);String geojson = writer.toString();writer.close();return geojson;}/*** GeoJson转几何对象* @param geojson* @return* @throws Exception*/public static Geometry geoJsonToGeometry(String geojson) throws Exception {return geometryJson.read(new StringReader(geojson));}}
2.ShapeFile文件读写工具类
package com.otitan.gz.bozhouly.forestry.industry.geotools;import com.otitan.gz.bozhouly.forestry.industry.IO.StringTokenReader;
import com.otitan.gz.bozhouly.forestry.industry.pojos.ShpDatas;
import com.otitan.gz.bozhouly.forestry.industry.pojos.ShpInfo;
import com.otitan.gz.bozhouly.forestry.industry.result.ResponseMessage;
import com.otitan.gz.bozhouly.forestry.industry.result.ResponseResult;
import org.geotools.data.*;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLD;
import org.geotools.styling.Style;
import org.geotools.swing.JMapFrame;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.locationtech.jts.geom.*;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;/*** <p>ShapeFile文件读写工具类</p>* @author carter* @blob https://blog.csdn.net/qq_21480329* @date*/
public class ShpTools {/**几何对象构造器【自定义的】*/private static GeometryCreator gCreator = GeometryCreator.getInstance();/**边界*/private static ReferencedEnvelope bounds;/**画布的宽度*/private static final int IMAGE_WIDTH = 1280;/**画布的高度*/private static final int IMAGE_HEIGHT = 1200;/*** 通过shp文件路径,读取shp内容* @param filePath 文件路径*/public static ShpDatas readShpByPath(String filePath,Integer limit) throws Exception {// 一个数据存储实现,允许从Shapefiles读取和写入ShapefileDataStore shpDataStore = new ShapefileDataStore(new File(filePath).toURI().toURL());// 设置编码【防止中文乱码】shpDataStore.setCharset(StandardCharsets.UTF_8);// getTypeNames:获取所有地理图层,这里我只取第一个【如果是数据表,取出的就是表名】String typeName = shpDataStore.getTypeNames()[0];System.out.println("shp【图层】名称:"+typeName);FeatureCollection<SimpleFeatureType, SimpleFeature> result = getFeatures(shpDataStore, typeName);// 迭代特征集合FeatureIterator<SimpleFeature> iterator = result.features();ShpDatas shpDatas = new ShpDatas();shpDatas.setName(typeName);shpDatas.setShpPath(filePath);buildShpDatas(limit, iterator, shpDatas);iterator.close();return shpDatas;}/*** 根据数据源及图层名称拿到特征集合* @param shpDataStore shp数据存储对象* @param typeName 图层名称* @return FeatureCollection*/private static FeatureCollection<SimpleFeatureType, SimpleFeature> getFeatures(ShapefileDataStore shpDataStore, String typeName) throws IOException {// 通过此接口可以引用单个shapefile、数据库表等。与数据存储进行比较和约束FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = shpDataStore.getFeatureSource(typeName);// 一个用于处理FeatureCollection的实用工具类。提供一个获取FeatureCollection实例的机制FeatureCollection<SimpleFeatureType, SimpleFeature> result = featureSource.getFeatures();System.out.println("地理要素【记录】:"+result.size()+"个");System.out.println("==================================");return result;}/*** 构建shpDatas对象* @param limit 要素查询限制数* @param iterator 迭代器* @param shpDatas shp封装的数据集*/private static void buildShpDatas(Integer limit, FeatureIterator<SimpleFeature> iterator, ShpDatas shpDatas) {// 这里我们只迭代前limit个int stop = 0;while (iterator.hasNext()) {if (stop > limit) {break;}// 拿到一个特征SimpleFeature feature = iterator.next();// 取出特征里面的属性集合Collection<Property> p = feature.getProperties();// 遍历属性集合Map<String,Object> prop = new HashMap<>();for (Property pro : p) {String key = pro.getName().toString();String val;if ("java.util.Date".equals(pro.getType().getBinding().getName())){SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");val = pro.getValue() ==null ? "" : dateFormat.format(pro.getValue());}else{val = pro.getValue()==null ?"":pro.getValue().toString();}prop.put(key, val);System.out.println("key【字段】:"+key+"\t||value【值】:"+val);}System.out.println("\n============================ 序号:"+stop+"\n");shpDatas.addProp(prop);stop++;} // end 最外层 while}/*** 将一个几何对象写进shapefile* @param filePath 文件路径* @param geometry 几何对象*/public static void writeShpByGeom(String filePath, Geometry geometry) throws Exception{ShapefileDataStore ds = getshpDS(filePath, geometry);FeatureWriter<SimpleFeatureType, SimpleFeature> writer = ds.getFeatureWriter(ds.getTypeNames()[0],Transaction.AUTO_COMMIT);// Interface SimpleFeature:一个由固定列表值以已知顺序组成的SimpleFeatureType实例。SimpleFeature feature = writer.next();feature.setAttribute("name", "XXXX名称");feature.setAttribute("path", "./test");feature.setAttribute("the_geom", geometry);feature.setAttribute("id", 1010L);feature.setAttribute("des", "XXXX描述");System.out.println("========= 写入【"+geometry.getGeometryType()+"】成功 !=========");// 写入writer.write();// 关闭writer.close();// 释放资源ds.dispose();}/*** 将一个几何对象写进shapefile* @param file 文件* @param map 数据*/public static void writeShpByGeom(File file, Map<String, Object> map, Geometry geometry) throws Exception{ShapefileDataStore ds = getshpDS(file, map, geometry);FeatureWriter<SimpleFeatureType, SimpleFeature> writer = ds.getFeatureWriter(ds.getTypeNames()[0],Transaction.AUTO_COMMIT);// Interface SimpleFeature:一个由固定列表值以已知顺序组成的SimpleFeatureType实例。SimpleFeature feature = writer.next();// for (AttributeDescriptor attr : feature.getFeatureType().getAttributeDescriptors()) {
// System.out.println("Schema 字段: " + attr.getLocalName());
// }Map<String, Object> sortedMap = new TreeMap<>(map);sortedMap.forEach((k, v) -> {
// System.out.println(k + ":" + v);feature.setAttribute(k, v);});// feature.setAttribute("the_geom", geometry);// feature.setAttribute("name", "XXXX名称");
// feature.setAttribute("path", "./test");
// feature.setAttribute("the_geom", geometry);
// feature.setAttribute("id", 1010L);
// feature.setAttribute("des", "XXXX描述");
//
// System.out.println("========= 写入【"+geometry.getGeometryType()+"】成功 !=========");// 写入writer.write();// 关闭writer.close();// 释放资源ds.dispose();}/*** 将一个几何对象写进shapefile* @param shpInfo shp信息*/public static ResponseResult writeShpByGeom(ShpInfo shpInfo) throws Exception{// 特殊字符串解析器StringTokenReader reader = new StringTokenReader();// 根据几何对象的wkt字符串,反解【解析】成Geometry对象Geometry geometry = reader.read(shpInfo.getGeom());// 拿到shp对象所在的目录【文件夹】String path = shpInfo.getPath();File file = new File(path);if(!file.exists()){file.mkdir();}if(!file.isDirectory()){return new ResponseResult(ResponseMessage.BAD_REQUEST,"path不是有效的文件夹" );}String filePath = shpInfo.getPath()+"/"+shpInfo.getName()+".shp";ShapefileDataStore ds = getshpDS(filePath, geometry);String typeName = ds.getTypeNames()[0];FeatureWriter<SimpleFeatureType, SimpleFeature> writer ;if(shpInfo.isAppendWrite()){// 追加写几何对象writer = ds.getFeatureWriterAppend(typeName, Transaction.AUTO_COMMIT);}else{// 覆盖写几何对象writer = ds.getFeatureWriter(typeName, Transaction.AUTO_COMMIT);}// Interface SimpleFeature:一个由固定列表值以已知顺序组成的SimpleFeatureType实例。SimpleFeature feature = writer.next();feature.setAttribute("name", shpInfo.getName());feature.setAttribute("path", shpInfo.getPath());feature.setAttribute("the_geom", geometry);feature.setAttribute("id", shpInfo.getId());feature.setAttribute("des", shpInfo.getDes());System.out.println("========= 写入【"+geometry.getGeometryType()+"】成功 !=========");// 写入writer.write();// 关闭writer.close();// 释放资源ds.dispose();// 返回创建成功后的shp文件路径return new ResponseResult(ResponseMessage.OK,filePath);}/*** 拿到配置好的DataStore* @param file 文件* @param geometry 几何对象* @return ShapefileDataStore*/private static ShapefileDataStore getshpDS(File file, Map<String, Object> map, Geometry geometry) throws IOException {Map<String, Serializable> params = new HashMap<>();// 1. 获取父目录File parentDir = file.getParentFile();// 2. 检查父目录是否存在,若不存在则创建if (parentDir != null && !parentDir.exists()) {parentDir.mkdirs();}// 2、用于捕获参数需求的数据类 URLP:url to the .shp file.params.put(ShapefileDataStoreFactory.URLP.key, file.toURI().toURL());// 3、创建一个新的数据存储【如果存在,则不创建】ShapefileDataStore ds = (ShapefileDataStore) new ShapefileDataStoreFactory().createNewDataStore(params);// 4、定义图形信息和属性信息 -- SimpleFeatureTypeBuilder 构造简单特性类型的构造器SimpleFeatureTypeBuilder tBuilder = new SimpleFeatureTypeBuilder();// 5、设置 -- WGS84:一个二维地理坐标参考系统,使用WGS84数据tBuilder.setCRS(DefaultGeographicCRS.WGS84);tBuilder.setName("shapefile");Map<String, Object> sortedMap = new TreeMap<>(map);sortedMap.forEach((k, v) -> {tBuilder.add(k, v.getClass());
// System.out.println(k + ":" + v.getClass());});// tBuilder.add("the_geom", geometry.getClass());// map.forEach((k, v) -> {
// tBuilder.add(k, v.getClass());
// });// 添加名称
// tBuilder.add("name", String.class);
// // 添加shp所在目录名称
// tBuilder.add("path", String.class);
// // 添加 一个几何对象
// tBuilder.add("the_geom", geometry.getClass());
// // 添加一个id
// tBuilder.add("id", Long.class);
// // 添加描述
// tBuilder.add("des", String.class);// 设置此数据存储的特征类型ds.createSchema(tBuilder.buildFeatureType());// 设置编码ds.setCharset(StandardCharsets.UTF_8);return ds;}/*** 拿到配置好的DataStore* @param filePath 文件路径* @param geometry 几何对象* @return ShapefileDataStore*/private static ShapefileDataStore getshpDS(String filePath, Geometry geometry) throws IOException {// 1.创建shape文件对象File file = new File(filePath);Map<String, Serializable> params = new HashMap<>();// 2、用于捕获参数需求的数据类 URLP:url to the .shp file.params.put(ShapefileDataStoreFactory.URLP.key, file.toURI().toURL());// 3、创建一个新的数据存储【如果存在,则不创建】ShapefileDataStore ds = (ShapefileDataStore) new ShapefileDataStoreFactory().createNewDataStore(params);// 4、定义图形信息和属性信息 -- SimpleFeatureTypeBuilder 构造简单特性类型的构造器SimpleFeatureTypeBuilder tBuilder = new SimpleFeatureTypeBuilder();// 5、设置 -- WGS84:一个二维地理坐标参考系统,使用WGS84数据tBuilder.setCRS(DefaultGeographicCRS.WGS84);tBuilder.setName("shapefile");// 添加名称tBuilder.add("name", String.class);// 添加shp所在目录名称tBuilder.add("path", String.class);// 添加 一个几何对象tBuilder.add("the_geom", geometry.getClass());// 添加一个idtBuilder.add("id", Long.class);// 添加描述tBuilder.add("des", String.class);// 设置此数据存储的特征类型ds.createSchema(tBuilder.buildFeatureType());// 设置编码ds.setCharset(StandardCharsets.UTF_8);return ds;}/*** 打开shp文件,获取地图内容* @param filePath 文件路径* @param isOpenByChoose 是否自定义打开shp文件* @throws Exception*/public static MapContent getMapContentByPath(String filePath,boolean isOpenByChoose,String color) throws Exception{File file;if(isOpenByChoose){// 1.1、 数据源选择 shp扩展类型的file = JFileDataStoreChooser.showOpenFile("shp", null);}else{// 1.2、根据路径拿到文件对象file = new File(filePath);}if(file==null){return null;}// 2、得到打开的文件的数据源FileDataStore store = FileDataStoreFinder.getDataStore(file);// 3、设置数据源的编码,防止中文乱码((ShapefileDataStore)store).setCharset(Charset.forName("UTF-8"));/*** 使用FeatureSource管理要素数据* 使用Style(SLD)管理样式* 使用Layer管理显示* 使用MapContent管理所有地图相关信息*/// 4、以java对象的方式访问地理信息 -- 简单地理要素SimpleFeatureSource featureSource = store.getFeatureSource();bounds = featureSource.getBounds();// 5、创建映射内容,并将我们的shapfile添加进去MapContent mapContent = new MapContent();// 6、设置容器的标题mapContent.setTitle("Appleyk's GeoTools");Color color1;if(color == null || "".equals(color.toLowerCase())){color1 = Color.BLACK;}else if("red".equals(color.toLowerCase())){color1 = Color.RED;}else if("green".equals(color.toLowerCase())){color1 = Color.GREEN;}else if("blue".equals(color.toLowerCase())){color1 = Color.BLUE;}else{color1 = Color.getColor(color);}// 7、创建简单样式 【颜色填充】Style style = SLD.createSimpleStyle(featureSource.getSchema(),color1);// 8、显示【shapfile地理信息+样式】Layer layer = new FeatureLayer(featureSource, style);// 9、将显示添加进map容器mapContent.addLayer(layer);return mapContent;}public static void showMap(MapContent mapContent){JMapFrame.showMap(mapContent);}/*** shp文件转Image【格式定png】* @param shpFilePath shp目标文件* @param destImagePath 转成图片的文件 == 如果没有,转成的图片写进response输出流里* @param response 响应流* @throws Exception*/public static void shp2Image(String shpFilePath,String destImagePath,String color, HttpServletResponse response) throws Exception{// 流渲染器StreamingRenderer renderer = new StreamingRenderer();MapContent mapContent = getMapContentByPath(shpFilePath,false,color );renderer.setMapContent(mapContent);Rectangle imageBounds = new Rectangle(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);BufferedImage dumpImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);Graphics2D g2d = dumpImage.createGraphics();g2d.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);renderer.paint(g2d, imageBounds, bounds);g2d.dispose();if(destImagePath == null || "".equals(destImagePath)){ImageIO.write(dumpImage, "png", response.getOutputStream());}else{ImageIO.write(dumpImage, "png", new File(destImagePath));}}public static void main(String[] args) throws Exception{// File file = ResourceUtils.getFile("classpath:static/shpTest[Point]/dp_tl.shp");
// // 从shp文件里面读取属性信息
// readShpByPath(file.getAbsolutePath(),10);System.out.println("=================下面开始往shp文件里面写几何对象===================");// 先创建文件夹testString filePath = "./test.shp";// String pointWkt="POINT (120.76164848270959 31.22001141278534)";
// Point point = gCreator.createPointByWKT(pointWkt);
//
// // Polygon【面】
// String polygonWkt="POLYGON ((103.859188 34.695908, 103.85661 34.693788, 103.862027 34.69259, 103.863709 34.695078, 103.859188 34.695908))";
// Polygon polygon = gCreator.createPolygonByWKT(polygonWkt);
//
// // LineString【线】
// String linestringWkt="LINESTRING(113.511315990174 41.7274734296674,113.51492087909 41.7284983348307,113.516079593384 41.727649586406,113.515907932007 41.7262243043929,113.514019656861 41.7247989907606,113.512131381714 41.7250872589898,113.51138036319 41.7256637915682,113.511315990174 41.7274734296674)";
// LineString lineString = gCreator.createLineByWKT(linestringWkt);// MultiPolygon【多面】String multiPolyWkt = "MULTIPOLYGON(((101.870371 25.19228,101.873633 25.188183,101.880564 25.184416,101.886808 25.186028,101.892043 25.189969,101.896592 25.190163,101.903716 25.190785,101.905454 25.193464,101.899897 25.196202,101.894146 25.197911,101.891657 25.19826,101.886078 25.197658,101.884211145538 25.2007060137013,101.88172564506 25.1949712942389,101.87874 25.199619,101.874641 25.200998,101.868547 25.202415,101.863741 25.202415,101.85887 25.202842,101.854557 25.202182,101.852604 25.199736,101.852282 25.19628,101.854492 25.194183,101.855608 25.192668,101.863698 25.192105,101.870371 25.19228)))";MultiPolygon multiPolygon = gCreator.createMulPolygonByWKT(multiPolyWkt);// 几何对象的范围【矩形边界】Envelope envelope = multiPolygon.getEnvelopeInternal();System.out.println(envelope);// 往shp文件里面写几何对象writeShpByGeom(filePath,multiPolygon);}}
3.导出实现
@GetMapping("/export/{gid}")public void exportShp(@PathVariable("gid") Integer gid, HttpServletResponse response) throws Exception {MatterRecordEntity matterRecord = matterRecordService.findMatterRecordByGid(gid);// 将PGobject转换为Geometry对象Geometry geometry = GeometryConverter.convertPGObjectToGeometry(matterRecord.getGeom());String businessCode = matterRecord.getBusinessCode() + "_" + new DateTime().getMillis();String filePath = fileShp + businessCode + "/";String fileName = matterRecord.getBusinessCode() + ".shp";File file = new File(filePath + fileName);JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(matterRecord));// Shapefile 默认的几何字段名为 the_geom,若导出时未显式指定或字段名被修改,部分软件(如 ArcGIS)可能无法识别几何数据jsonObject.put("the_geom", geometry);Map<String, Object> map = new HashMap<>();jsonObject.forEach((k, v) -> {if (k.length() > 10) {return;}map.put(k, v);});// 往shp文件里面写几何对象writeShpByGeom(file, map, geometry);File sourceDir = new File(filePath);// String fileNameZip = URLEncoder.encode(matterRecord.getBusinessCode() + ".zip", StandardCharsets.UTF_8.name());
// response.setContentType("application/zip");
// response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
// "attachment; filename=" + fileNameZip);// 获取Servlet的输出流,用于写入ZIP文件内容ServletOutputStream outputStream = response.getOutputStream();ZipOutputStream zos = new ZipOutputStream(outputStream);
// addDirectoryToZip(sourceDir, zos, sourceDir.getName());compressDirFlat(sourceDir, zos);String fileNameZip = URLEncoder.encode(businessCode + ".zip", StandardCharsets.UTF_8.name());response.setContentType("application/zip");response.setHeader(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename=" + fileNameZip);zos.close(); // 关闭ZipOutputStream,这将触发所有缓冲的数据被写入输出流outputStream.flush(); // 刷新输出流,确保所有数据都被发送}private void compressDirFlat(File dir, ZipOutputStream zos) throws IOException {for (File file : dir.listFiles()) {if (file.isHidden()) continue;if (file.isDirectory()) {compressDirFlat(file, zos); // 递归处理子目录} else {addFileEntry(file, zos);}}}private void addFileEntry(File file, ZipOutputStream zos) throws IOException {try (FileInputStream fis = new FileInputStream(file)) {// 仅保留文件名,忽略路径ZipEntry entry = new ZipEntry(file.getName());zos.putNextEntry(entry);// 带缓冲的流复制byte[] buffer = new byte[4096];int len;while ((len = fis.read(buffer)) > 0) {zos.write(buffer, 0, len);}zos.closeEntry();}}
注意事项Shapefile 默认的几何字段名为 the_geom,若导出时未显式指定或字段名被修改,部分软件(如 ArcGIS、QGI)可能无法识别几何数据。
4.导出效果截图
5.相关大数据学习demo地址:
https://github.com/carteryh/big-data
相关文章:
java导出postgis空间数据几何对象shapefile文件
项目开发中,需要java后端实现导出postgis空间数据几何对象shapefile文件,以便能直观查看数据详情。注意事项Shapefile 默认的几何字段名为 the_geom,若导出时未显式指定或字段名被修改,部分软件(如 ArcGIS、QGI&#x…...
蓝桥杯嵌入式按键长按双击
直接上代码这个代码里面我们简单实现了如果按键按下时间超过0.8秒K1的值增加,短按只增加一次,按键2长按K2值增加,按键3双击K1的值减1,按键4双击K2的值减1 #include "fun.h" #define long_press_time 800//定义长按时间…...
深入解析Java中的栈:从JVM原理到开发实践
一、栈的双重身份:JVM运行时数据区 vs 数据结构 1. JVM层面的栈 线程私有:每个线程独立拥有自己的栈 LIFO结构:后进先出的方法调用模型 栈帧存储:每个方法对应一个栈帧(Stack Frame) 2. 数据结构中的栈…...
408 计算机网络 知识点记忆(6)
前言 本文基于王道考研课程与湖科大计算机网络课程教学内容,系统梳理核心知识记忆点和框架,既为个人复习沉淀思考,亦希望能与同行者互助共进。(PS:后续将持续迭代优化细节) 往期内容 408 计算机网络 知识…...
从ETL到ELT:大数据时代下两者的选型建议及优势
随着大数据时代的到来,数据量呈爆炸式增长,数据类型日益复杂,ETL与ELT两种技术路径的抉择直接影响着数据处理效率。我们这次来深入解析下两种模式的本质差异与应用场景,为企业提供选型建议。 一、ETL架构的优势 ETL架构遵循“提…...
Java蓝桥杯习题一:for循环和字符串的应用
知道循环次数用for循环 练习题1 小明对数位中含有2.0.1.9的数字很感兴趣,在1到40中这样的数包含1.2.9.10至32.39.40,共28个,他们的和是574.请问,在1到2019中,所有这样的数的和是多少?(2019Jav…...
Windows 图形显示驱动开发-WDDM 2.0功能_分配用法跟踪
随着分配列表的消失,视频内存管理器 (VidMm) 不再能够查看特定命令缓冲区中引用的分配。 因此,VidMm 不再能够跟踪分配使用情况和处理相关同步。 此责任现在由用户模式驱动程序 (UMD) 承担。 具体而言,UMD 需要处理与直接 CPU 访问分配和重命…...
SpringMVC的请求-文件上传
文件上传客户端三要素 1. 表单项type“file” 2. 表单的提交方式是post 3. 表单的enctype属性是多部分表单形式,及enctype“multipart/form-data” <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <he…...
MySQL表的增删查改(基础)
一.插入数据 数据准备 create table student(id INT,sn INT comment 学号,name VARCHAR(20) comment 姓名,qq_mail VARCHAR(20) comment QQ邮箱 ); 1.单行数据全列插入 INSERT INTO student VALUES (100, 10000, 唐三藏, NULL); INSERT INTO student VALUES (101, 10001, …...
C++初阶-C++的讲解1
目录 1.缺省(sheng)参数 2.函数重载 3.引用 3.1引用的概念和定义 3.2引用的特性 3.3引用的使用 3.4const引用 3.5.指针和引用的关系 4.nullptr 5.总结 1.缺省(sheng)参数 (1)缺省参数是声明或定义是为函数的参数指定一个缺省值。在调用该函数是…...
【NLP 面经 9.逐层分解Transformer】
如果我能给你短暂的开心 —— 25.4.7 一、Transformer 整体结构 1.Tranformer的整体结构 Transformer 的整体结构,左图Encoder和右图Decoder,下图是Transformer用于中英文翻译的整体结构: 可以看到 Transformer 由 Encoder 和 Decoder 两个…...
Diffusion Policy Visuomotor Policy Learning via Action Diffusion官方项目解读(二)(5)
运行官方代码库中提供的Colab代码:vision-based environment(二)(5) Network十八、类SinusoidalPosEmb,继承自nn.Module十八.1 def __init__()十八.2 def forward()总体说明 十九、类Downsample1dÿ…...
西门子S7-1200PLC 工艺指令PID_Temp进行控温
1.硬件需求: 西门子PLC:CPU 1215C DC/DC/DC PLC模块:SM 1231 TC模块 个人电脑:已安装TIA Portal V17软件 加热套:带加热电源线以及K型热电偶插头 固态继电器:恩爵 RT-SSK4A2032-08S-F 其他࿱…...
【深度学习:理论篇】--Pytorch进阶教程
目录 1.神经网络 1.1.torch.nn 核心模块 1.2.定义神经网络 1.3.损失函数 1.4.反向传播 1.5.梯度更新 2.图片分类器 2.1.数据加载 2.2.卷积神经网络 2.3.优化器和损失 2.4.训练网络 2.5.测试网络 2.6.GPU上训练 3.数据并行训练--多块GPU 3.1.导入和参数 3.2.构造…...
卷积神经网络(CNN)基础
目录 一、应用场景 二、卷积神经网络的结构 1. 输入层(Input Layer) 2. 卷积层(Convolutional Layer) 3. 池化层(Pooling Layer) 最大池化(max_pooling)或平均池化(…...
第 28 场 蓝桥入门赛 JAVA 完整题解
前言 本文总结了六个编程题目的解题思路与核心考点,涵盖基础语法、逻辑分析、贪心算法、数学推导等知识点。每个题目均从问题本质出发,通过巧妙的算法设计或数学优化降低复杂度,展现了不同场景下的编程思维与解题技巧。以下为各题的详细考点解…...
Python 网络请求利器:requests 包详解与实战
诸神缄默不语-个人技术博文与视频目录 文章目录 一、前言二、安装方式三、基本使用1. 发起 GET 请求2. 发起 POST 请求 四、requests请求调用常用参数1. URL2. 数据data3. 请求头 headers4. 参数 params5. 超时时间 timeout6. 文件上传 file:上传纯文本文件流7. jso…...
聊透多线程编程-线程基础-1.进程、线程基础概念
目录 一、进程 二、线程 三、进程与线程的关系 四、进程与线程的比较 注:本文多张图片来源于网络,如有侵权,请联系删除 一、进程 1. 进程的定义 进程是指在系统中正在运行的一个应用程序的实例,是操作系统进行资源分配和调…...
Android:Android Studio右侧Gradle没有assembleRelease等选项
旧版as是“Do not build Gradle task list during Gradle sync” 操作这个选项。 参考这篇文章:Android Studio Gradle中没有Task任务,没有Assemble任务,不能方便导出aar包_gradle 没有task-CSDN博客 在as2024版本中,打开Setting…...
LeetcodeBST2JAVA
235.二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大&…...
如何创建单独的城市活码?活码能永久使用吗?
如何创建单独的城市活码 创建单独的城市活码通常需要借助专业的第三方工具,以下是具体步骤: 1.选择合适的工具 推荐使用专业的活码生成工具。 2.注册并登录 访问官网,完成注册并登录。 3.创建活码 在首页点击“创建活码”按钮。输入活码…...
用户画像(https://github.com/memodb-io/memobase)应用
1.下载项目的源代码,我们要先启动后端,用docker启动 cd src/server cp .env.example .env cp ./api/config.yaml.example ./api/config.yaml 这里我的配置内容如下config.yaml(因为我是调用的符合openai格式的大模型,所以我没改,如果要是别的大模型的话,需要自己再做兼容…...
基于形状补全和形态测量描述符的腓骨游离皮瓣下颌骨重建自动规划|文献速递-深度学习医疗AI最新文献
Title 题目 Automated planning of mandible reconstruction with fibula free flap basedon shape completion and morphometric descriptors 基于形状补全和形态测量描述符的腓骨游离皮瓣下颌骨重建自动规划 01 文献速递介绍 因创伤、骨髓炎和肿瘤而接受下颌骨节段切除术…...
Python3笔记之号称替代pip的uv包管理器
uv是什么? uv,这是一个由 Astral 团队开发的极快速的Python包和项目管理工具,用Rust语言编写。它集成了多种功能,旨在替代pip、pip-tools、pipx、poetry、pyenv、twine、virtualenv等多个工具,提供更高效、更全面的Py…...
面试如何应用大模型
在面试中,如果被问及如何应用大模型,尤其是面向政务、国有企业或大型传统企业的数字化转型场景,你可以从以下几个角度进行思考和回答: 1. 确定应用大模型的目标与痛点 首先,明确应用大模型的业务目标,并结合企业的实际需求分析可能面临的痛点。这些企业通常会关注如何提…...
贪心算法:部分背包问题深度解析
简介: 该Java代码基于贪心算法实现了分数背包问题的求解,核心通过单位价值降序排序和分阶段装入策略实现最优解。首先对Product数组执行双重循环冒泡排序,按wm(价值/重量比)从高到低重新排列物品;随后分两阶段装入:循环…...
Java程序的基本规则
java程序的基本规则 1.1 java程序的组成形式 Java程序是一种纯粹的面向对象的程序设计语言,因此Java程序 必须以类(class)的形式存在,类(class)是Java程序的最小程序 单位。Java程序不允许可执行性语句…...
机器学习-线性回归模型
机器学习-线性回归模型 线性模型笔记1、向量化2、线性回归模型公式3、损失函数(代价函数)4、梯度下降法5、Python 实现示例 6、使用 sklearn 实现线性回归模型✅ 基本步骤如下:📦 示例代码: 7、numpy中的切片X[n,:]是取…...
Linux 入门指令(1)
(1)ls指令 ls -l可以缩写成 ll 同时一个ls可以加多个后缀 比如 ll -at (2)pwd指令 (3)cd指令 cd .是当前目录 (4)touch指令 (5)mkdir指令 (6)rmdir和rm…...
密码学基础——AES算法
目录 一、算法背景 AES算法与Rijndael算法 二、算法特点 1.安全性高 2.效率高 3.灵活性好 三、算法说明 3.1状态、种子密钥和轮数的概念 (1)状态(State) 定义 结构:通常状态是一个 4N 字节的矩阵࿰…...
淘宝API与小程序深度联动:商品详情页“一键转卖”功能开发
要实现淘宝 API 与小程序深度联动,开发商品详情页 “一键转卖” 功能,可按以下步骤进行: 1. 前期准备 淘宝开放平台接入:在淘宝开放平台注册开发者账号,创建应用,获取 App Key 和 App Secret,…...
深入解析 C++ 设计模式:原理、实现与应用
一、引言 在 C 编程的广袤领域中,设计模式犹如闪耀的灯塔,为开发者指引着构建高效、可维护软件系统的方向。设计模式并非神秘莫测的代码魔法,实际上,我们在日常编程中或许早已与之打过交道。简单来说,设计模式常常借助…...
配置与管理代理服务器
安装squid Squid软件包在标准yum存储库中可用,因此,我们正在使用yum命令安装Squid代理。 [rootserver ~]# dnf install -y squid //安装 [rootserver ~]#systemctl enable --now squid.service [rootserver ~]#systemctl status squid.serv…...
RuntimeError: CUDA error: invalid device function
CUDA内核编译时的架构设置与当前GPU不兼容导致 -- The CUDA compiler identification is NVIDIA 11.5.119 (实际为 12.6) 解决方案: 1. 查看显卡计算能力 2. CMakeLists.txt 修改 set_target_properties(my_library PROPERTIESCUDA_AR…...
vulnhub:sunset decoy
靶机下载地址https://www.vulnhub.com/entry/sunset-decoy,505/ 渗透过程 简单信息收集 nmap 192.168.56.0/24 -Pn # 确定靶机ip:192.168.56.121 nmap 192.168.56.121 -A -T4 # 得到开放端口22,80 在80端口得到save.zip,需要密码解压。 john破解压缩…...
MySQL日期时间类型详解:DATE、TIME和DATETIME的用法与区别
在数据库设计中,正确处理日期和时间数据是至关重要的。MySQL提供了多种数据类型来存储时间信息,其中最常用的三种是DATE、TIME和DATETIME。本文将详细介绍这三种类型的特性、区别以及实际应用场景。 一、基本数据类型介绍 1. DATE类型 用途࿱…...
js异步机制
1、什么是异步机制?为什么js需要异步机制? 异步机制和同步机制是相对应的,异步是指:当代码按照顺序执行到一些比较耗时的操作,不会立刻执行,而是将这些操作推到一个队列中等待合适的时机从队列中取出任务执…...
Pycharm常用快捷键总结
主要是为了记录windows下的PyCharm的快捷键,里面的操作都试过了功能描述会增加备注。 文件操作 快捷键功能描述Ctrl N新建文件Ctrl Shift N根据名称查找文件Ctrl O打开文件Ctrl S保存当前文件Ctrl Shift S另存为Alt F12打开终端(Terminal&…...
巧记英语四级单词 Unit2-下【晓艳老师版】
mit传递(send 送)、 superiority n.优势,优越性 超越别人的东西就是自己的优势govern v.统治 government政府 统治的机构administer v.管理,治理 minister 大臣 部长,mini-小人,一再的做大臣 部长…...
走进底层 - JVM工作原理入门指南
走进底层 - JVM工作原理入门指南 Java 之所以能够实现“一次编写,到处运行”(Write Once, Run Anywhere, WORA),核心在于 Java 虚拟机(JVM, Java Virtual Machine)。JVM 是 Java 程序的运行环境,…...
windows 10频繁通知A字“出现了问题,无法安装功能。”
一、故障突现 windows 10频繁通知A字“出现了问题,无法安装功能。” 编辑文档时发现黑体、楷体gb_2312等常用字体,在字体列表中失踪,原来设置好的字体也显示失效。 二、起因分析 回想了一下,是3月27日安装了 2025-适用于Windows…...
基础环境配置
1.GitGerritjenkins Linux 远程登录 | 菜鸟教程 https://zhuanlan.zhihu.com/p/22766058062 2.Samba 配置 3.软件安装 (1)MobaXterm (2)Vscode (3)Xmind (4) Audacity Aud…...
ROS2——foxy apt打包离线安装deb包
需要从A设备复制ROS2环境到B设备,且B设备有可能没网络,所以选择制作离线资源包 1. 本机安装指令 本机环境ubuntu20.04,安装ros2-foxy版本,直接输入以下指令,基本不会遇到问题 这里安装的是ros-base版本,不…...
大数据学习(104)-clickhouse与hdfs
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...
大数据 - 1. 概述
早期的计算机(上世纪70年代前) 是相互独立的,各自处理各自的数据上世纪70年代后,出现了基于TCP/IP协议的小规模的计算机互联互通。上世纪90年代后,全球互联的互联网出现。当全球互联网逐步建成(2000年左右&…...
CD25.【C++ Dev】类和对象(16) static成员(上)
目录 1.static成员变量 问题:实现一个类,计算程序中创建出了多少个类对象 设计思路 代码示例 版本1 版本2 static成员 特点1.static成员为静态成员,为所有类对象所共享(在某种程度上可以理解为全局的,用类去封装"全局变量"),存放在静态区,则不属于某个具体的…...
C语言今天开始了学习
好多年没有弄了,还是捡起来弄下吧 用的vscode 建议大家参考这个配置 c语言vscode配置 c语言这个语言简单,但是今天听到了一个消息说python 不知道怎么debug。人才真多啊...
Mockito如何对静态方法进行测试
在 Mockito 中,直接对静态方法进行模拟是困难的,因为 Mockito 的设计理念是优先通过依赖注入(DI)管理对象,而静态方法破坏了这种设计(难以解耦)。不过,从 Mockito 3.4.0 版本开始,通过 mockStatic 方法支持了对静态方法的模拟(需配合 mockito-inline 依赖)。 从 Mo…...
Three.js 入门实战:安装、基础概念与第一个场景⭐
学习本章节你不必要追求细节,你只需要了解基本的3D场景需要哪些元素组成,如何通过组成3D场景的元素属性调整来控制3D物体或者场景即可。 在上一篇文章中我们初识了 Three.js,今天我们正式进入实战环节 🎯 前置准备: …...
【QT】QT的消息盒子和对话框(自定义对话框)
QT的消息盒子和对话框(自定义对话框) 一、消息盒子QMessageBox1、弹出警告盒子示例代码:现象: 2、致命错误盒子示例代码:现象: 3、帮助盒子示例代码:现象: 4、示例代码: …...