1.问题,在Gis的项目中我们会经常用到有关于shp的读取,导入导出的功能,为此公司大牛做了一个工具,简化了很多操作,只要学会应用即可

2.使用

ShapeTools,工具类,里面封装了各种处理shp的方法
public class ShapeTools<T> {/*** shp文件定义字段时,字段名长度不能多于10个字符*/private final int FEILD_LENGTH_LIMIT = 10;/*** shp文件定义字段时,空间属性字段必须是the_geom*/private final String GEOM_FIELD_NAME = "the_geom";/*** 2017年11月23日* 读取shp文件,把名称和fid保存到ES中,* @param fileName* @param url 例: http://localhost:9200/dmdz2017/poi/_bulk </br>*            通过http  _bulk命令批量建索引的方式* @throws MalformedURLException* @throws IOException*/public void readShpToES(String fileName, String url) throws MalformedURLException, IOException {ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();WKTReader reader = new WKTReader();File file = new File(fileName);ShapefileDataStore sds = (ShapefileDataStore) dataStoreFactory.createDataStore(file.toURI().toURL());sds.setCharset(Charset.forName("GBK"));List<Map<String, String>> fs = new ArrayList<>();SimpleFeatureSource featureSource = sds.getFeatureSource();SimpleFeatureIterator iterator = featureSource.getFeatures().features();Property per = null;while(iterator.hasNext()){SimpleFeature feature = iterator.next();Iterator<Property> it = feature.getProperties().iterator();Map<String, String> properties = new HashMap<>();while(it.hasNext()){per = it.next();String key = per.getName().toString();properties.put(key, per.getValue().toString());}fs.add(properties);}shpToES(fs, url);}/*** 文件名加上时间戳* @param fname* @return*/private String rename(String fname){int index = fname.lastIndexOf(".");String temp = fname.substring(0, index) + System.currentTimeMillis();temp += fname.substring(index);return temp;}/*** 使用Apache http建立ES索引* @param fs* @param url http://localhost:9200/dmdz2017/poi/_bulk* @return* @throws IOException*/private boolean shpToES(List<Map<String, String>> fs, String url) throws IOException{if(null == url || "".equals(url)) return false;long startTime = System.currentTimeMillis();PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(cm).build();HttpPost httpPost = new HttpPost(url);StringBuilder sb = new StringBuilder();//Map<String, String> prs = null;for(int i = 0, total = fs.size(); i < total; i++){prs = fs.get(i);sb.append("{\"create\":{\"_id\":\"").append(startTime + i).append("\"}}\n");sb.append(toJson(prs)).append("\n");//System.out.println(sb.toString());if((i % 200) == 0 || i == total-1){httpPost.setEntity(new StringEntity(sb.toString(), "utf-8"));CloseableHttpResponse response = httpclient.execute(httpPost);response.close();sb = null;sb = new StringBuilder();System.out.println("数据入库完成,总共"+total+",已完成" + i + ", 耗时 " + (System.currentTimeMillis() - startTime) + "毫秒");}}httpclient.close();return true;}private String toJson(Map<String, String> ms){if(null == ms) {return "{}";}StringBuilder sb = new StringBuilder("{");for(Iterator<String> iterator = ms.keySet().iterator(); iterator.hasNext(); sb.append(",")){String k = iterator.next();sb.append("\"").append(k).append("\":\"").append(ms.get(k)).append("\"");}sb.deleteCharAt(sb.lastIndexOf(","));sb.append("}");return sb.toString();}/*** 一个测试创建shp的方法* @throws SchemaException* @throws ParseException* @throws IOException*/@Deprecatedprotected void testCreateFeature() throws SchemaException, ParseException, IOException{final SimpleFeatureType featureType = DataUtilities.createType("Location","the_geom:MultiPolygon:4490,"+ "name:String,"+ "type:String,"+ "typeCode:String");List<SimpleFeature> features = new ArrayList<>();SimpleFeatureBuilder sfb = new SimpleFeatureBuilder(featureType);WKTReader wktReader = new WKTReader();Geometry g = wktReader.read("MULTIPOLYGON(((120.245550628244 30.3697319771691,120.245604273926 30.3693135531255,120.24339413664 30.3690667936267,120.243630170808 30.368079739938,120.24205303449 30.3678007917758,120.241409305793 30.3668673845079,120.241291288517 30.3659447050454,120.241838457153 30.3652902449766,120.24261093291 30.3647216149009,120.242857696301 30.3633590530615,120.242986442154 30.3626831362105,120.246097798899 30.3664382241636,120.249713410056 30.3696568698573,120.249788512627 30.3709336004773,120.249482741414 30.3710677121942,120.247229688992 30.3708906898944,120.247353069806 30.3699948315325,120.245513078403 30.3698285368174,120.245550628244 30.3697319771691,120.245550628244 30.3697319771691)),((120.250083554427 30.3672053292808,120.250094283151 30.367993898842,120.249713410056 30.3696568698573,120.247953884385 30.3681011910327,120.24685954468 30.3670712245555,120.247661524439 30.3666179298771,120.248780002808 30.365907142485,120.249450554592 30.366513321681,120.250083554427 30.3672053292808,120.250083554427 30.3672053292808,120.250083554427 30.3672053292808)))");sfb.add(g);sfb.add("ming cheng");sfb.add("lei xing");sfb.add("type code");features.add(sfb.buildFeature("idididi"));ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();Map<String, Serializable> params = new HashMap<>();File f = new File("d:\\test.shp");params.put("url", f.toURI().toURL());params.put("create spatial index", Boolean.TRUE);ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);newDataStore.createSchema(featureType);String typeName = newDataStore.getTypeNames()[0];SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;SimpleFeatureCollection collection = new ListFeatureCollection(featureType, features);Transaction transaction = new DefaultTransaction("create");featureStore.setTransaction(transaction);featureStore.addFeatures(collection);transaction.commit();}/*** 指定创建shp文件的字段属性,注意字段名称长度不能超过10个字符</br>* 2018年11月2日,除了空间属性外,此方法只能生成全字符类型的其他属性* @param heads* @param geomName* @param geomClass* @return*/@Deprecatedpublic SimpleFeatureTypeBuilder createFeatureTypeBuilder(String[] heads, String geomName, Class geomClass){SimpleFeatureTypeBuilder sftb = new SimpleFeatureTypeBuilder();for(String name : heads){if(name.equals(geomName)){sftb.add(GEOM_FIELD_NAME, geomClass);} else {sftb.add(name, String.class);}}return sftb;}/*** 2018年10月29日* 如果是为了生成shp文件而调用此方法,注意字段名称长度不能超过10个字符* @param clazz 有com.trgis.geotools.GeoJSON注解的空间类* @return*/public SimpleFeatureTypeBuilder createFeatureTypeBuilder(Class clazz){SimpleFeatureTypeBuilder sftb = new SimpleFeatureTypeBuilder();String name = ((GeoJSON)clazz.getAnnotation(GeoJSON.class)).name();if(null == name){sftb.setName("feature");} else {sftb.setName(name);}boolean hasGeometry = false;Field[] fs = clazz.getDeclaredFields();for(Field f : fs){GeoJSON g = f.getAnnotation(GeoJSON.class);if(null == g)continue;String field = g.field();if("".equals(field)){field = f.getName();}if(!hasGeometry && g.isGeometry()) {hasGeometry = true;field = GEOM_FIELD_NAME;}Class type = g.type();if(type == Object.class)sftb.add(field, f.getType());elsesftb.add(field, type);}if(!hasGeometry)throw new NullPointerException("GeoJSON : annotation must has geometry field");return sftb;}/*** 将封装好的空间数据及属性写入到shp文件* @param tb* @param list 每个属性对应的数据对象,属性名称不能超过10个字符* @param filePath* @return* @throws IOException*/public boolean writeToShpFile(SimpleFeatureTypeBuilder tb, List<Map<String, Object>> list, String filePath) throws Exception {boolean b = false;Map<String, Serializable> params = new HashMap<String, Serializable>();FileDataStoreFactorySpi factory = new ShapefileDataStoreFactory();params.put(ShapefileDataStoreFactory.URLP.key,new File(filePath).toURI().toURL());ShapefileDataStore ds = (ShapefileDataStore) factory.createNewDataStore(params);tb.setName("shapefile");SimpleFeatureType sft = tb.buildFeatureType();ds.createSchema(sft);ds.setCharset(Charset.forName("GBK"));FeatureWriter<SimpleFeatureType, SimpleFeature> writer = ds.getFeatureWriter(Transaction.AUTO_COMMIT);//list中对象写入feature对象for (int i = 0; i < list.size(); i++){SimpleFeature feature = writer.next();for(Map.Entry<String, Object> entry : list.get(i).entrySet()){if(entry.getKey().length() > FEILD_LENGTH_LIMIT) throw new Exception("定义shp文件的属性字段名不能超过10个字符:" + entry.getKey());if(null != feature.getProperty(entry.getKey()))feature.setAttribute(entry.getKey(), entry.getValue());}}writer.write();writer.close();ds.dispose();b = true;return b;}/*** 将空间对象集合保存到shp文件,空间对象必须是有GeoJSON注解的* 2018年11月1日* @param objs* @param filePath* @return* @throws Exception* @author 王风雨*/public boolean writeToShpFromGeoJSON (List<T> objs, String filePath) throws Exception {boolean b = false;if(objs.size() == 0)return b;Class clazz = objs.get(0).getClass();GeoJSON g = objs.get(0).getClass().getAnnotation(GeoJSON.class);if(null == g)return b;SimpleFeatureTypeBuilder simpleFeatureTypeBuilder = new SimpleFeatureTypeBuilder();simpleFeatureTypeBuilder.setName(g.name());boolean hasGeometry = false;Field[] fields = clazz.getDeclaredFields();Map<String, String> fieldMap = new HashMap<>();for(Field f : fields){g = f.getAnnotation(GeoJSON.class);if(null == g)continue;String field = g.field();if("".equals(field)){field = f.getName();}if(field.length() > FEILD_LENGTH_LIMIT)throw new Exception("定义shp文件的属性字段名不能超过10个字符" + field);if(!hasGeometry && g.isGeometry()) {hasGeometry = true;field = GEOM_FIELD_NAME;}Class type = g.type();if(type == Object.class)simpleFeatureTypeBuilder.add(field, f.getType());elsesimpleFeatureTypeBuilder.add(field, type);fieldMap.put(field, f.getName());}if(!hasGeometry)throw new NullPointerException("GeoJSON : annotation must has geometry field");List<Map<String, Object>> list = new ArrayList<>();for(T t : objs){Map<String, Object> obj = new HashMap<>();for(Map.Entry<String, String> fieldEntry : fieldMap.entrySet()){Method method = clazz.getMethod(getMethodName(fieldEntry.getValue()));obj.put(fieldEntry.getKey(), method.invoke(t));}list.add(obj);}b = writeToShpFile(simpleFeatureTypeBuilder, list, filePath);return b;}/*** 读取shp文件,返回对象集合* @param filePath 文件名及全路径* @param shpEntityBuilder feature对象转换成指定对象的接口* @return* @throws Exception*/public List<T> readShp(String filePath, ShpEntityBuilder<T> shpEntityBuilder) throws Exception {ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();File file = new File(filePath);ShapefileDataStore sds = (ShapefileDataStore) dataStoreFactory.createDataStore(file.toURI().toURL());sds.setCharset(Charset.forName("GBK"));SimpleFeatureIterator iterator = sds.getFeatureSource().getFeatures().features();List<T> datas = new ArrayList<>();while (iterator.hasNext()){T data = shpEntityBuilder.createEntity(iterator.next());if(null != data)datas.add(data);}return datas;}/*** 将有GeoJSON注解的空间对象转换成SimpleFeature* @param obj 只针对有GeoJSON注解的字段* @return* @throws IOException* @throws InvocationTargetException* @throws IllegalAccessException* @throws NoSuchMethodException*/public SimpleFeature toSimpleFeature(Object obj) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {Class clazz = obj.getClass();SimpleFeatureTypeBuilder simpleFeatureTypeBuilder = createFeatureTypeBuilder(clazz);SimpleFeatureBuilder simpleFeatureBuilder = new SimpleFeatureBuilder(simpleFeatureTypeBuilder.buildFeatureType());String id = null;Map<String, Object> values = new HashMap<>();Field[] fields = clazz.getDeclaredFields();for(Field field : fields){GeoJSON g = field.getAnnotation(GeoJSON.class);if(null == g)continue;if(g.isID()){Method method = clazz.getMethod(getMethodName(field.getName()));id = method.invoke(obj).toString();} else {String fieldName =  g.field();if("".equals(fieldName)){fieldName = field.getName();}if(g.isGeometry()){fieldName = GEOM_FIELD_NAME;}Method method = clazz.getMethod(getMethodName(field.getName()));values.put(fieldName, method.invoke(obj));}}if(null == id)id = String.valueOf(System.currentTimeMillis());SimpleFeature feature = simpleFeatureBuilder.buildFeature(id);for (Map.Entry<String, Object> entry : values.entrySet()){feature.setAttribute(entry.getKey(), entry.getValue());}return feature;}/*** 将空间类集合转换为FeatureCollection对象* @param list 必须是有GeoJSON注解的空间类对象集合* @return* @throws InvocationTargetException* @throws NoSuchMethodException* @throws IllegalAccessException* @throws IOException*/public SimpleFeatureCollection toSimpleFeatureCollection(List<Object> list) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, IOException {DefaultFeatureCollection featureCollection = new DefaultFeatureCollection();for(Object obj : list){featureCollection.add(toSimpleFeature(obj));}return featureCollection;}/*** 将有GeoJSON注解的空间对象转换成GeoJSON格式字符串* @param obj 只针对有GeoJSON注解的字段* @return* @throws IOException* @throws InvocationTargetException* @throws IllegalAccessException* @throws NoSuchMethodException*/public String toGeoJSON(Object obj) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {SimpleFeature feature = toSimpleFeature(obj);StringWriter writer = new StringWriter();org.geotools.geojson.GeoJSON.write(feature, writer);return writer.toString();}private String getMethodName(String field){char first = field.charAt(0);String str = String.valueOf(first).toUpperCase();String name = field.replaceFirst(String.valueOf(first), str);return "get" + name;}/*** 由Feature对象生成指定对象的接口,get Feature对象的属性值 set 到指定对象,前提是知道属性名称和数据类型的对应关系* 例如 poi.setName(feature.getAttribute("name"))* @param <T>*/public interface ShpEntityBuilder<T> {/*** 根据字段对应关系,生成指定对象* @param feature* @return* @throws Exception*/public T createEntity(SimpleFeature feature) throws Exception;}private  String getSetMethodName(String field){char first = field.charAt(0);String str = String.valueOf(first).toUpperCase();String name = field.replaceFirst(String.valueOf(first), str);return "set" + name;}/*** 2018年11月2日* 传递GeoJSON注释的类,使用注解的对应关系将shp文件读取的数据转换成指定对象* @param clazz* @return*/public ShpEntityBuilder<T> useDefaultBuilder(Class clazz) {return feature -> {GeoJSON g = (GeoJSON) clazz.getAnnotation(GeoJSON.class);if(null == g)throw new Exception("非GeoJSON注解的类");T t = (T)clazz.newInstance();Field[] fields = clazz.getDeclaredFields();for(Field f : fields){g = f.getAnnotation(GeoJSON.class);if(null == g)continue;Method method = clazz.getMethod(getSetMethodName(f.getName()), f.getType());method.invoke(t, feature.getAttribute(g.isGeometry() ? GEOM_FIELD_NAME : "".equals(g.field()) ? f.getName() : g.field()));}return t;};}
}
GeoJSON类 空间对象进行GeoJSON格式字符化
package com.trgis.geotools;import java.lang.annotation.*;/*** ShapeTools工具类的toGeoJSON方法对空间对象进行GeoJSON格式字符化时使用</br>* 用来生成org.geotools.feature.simple.SimpleFeatureTypeBuilder* 2018年10月29日* @author */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@Documented
@Inherited
public @interface GeoJSON {/*** 对应的SimpleFeatureTypeBuilder的setName方法参数,一般用在类上** @return*/public String name() default "feature";/*** 用在空间对象的属性上,对应GeoJSON的属性名称</br>* 因为生成shp文件时,定义的shp字段名不能超过10个字符</br>* 所以当对象数据名长度超过10个字符时,必须指定field* @return*/public String field() default "";/*** 在空间属性上使用应设置为true,且field需要设置为the_geom* @return*/public boolean isGeometry() default false;/*** ID属性上使用应设置为true* @return*/public boolean isID() default false;/*** 对象的Class类型,当int, double等时,需要声明为Integer,Double等</br>* <code>@GeoJSON(type=Integer.class)</code>* <code>int count</code>* @return*/public  Class type() default  Object.class;
}

poi类

package com.trgis.geotools.model;import com.trgis.geotools.GeoJSON;
import com.vividsolutions.jts.geom.Point;/*** 2018/11/2** @author */
@GeoJSON(name = "poi")
public class Poi {/*** 类型是int double等时,必须指定type为包装类*/@GeoJSON(isID = true, type = Integer.class)private int id;@GeoJSON(isGeometry = true)private Point location;@GeoJSONprivate String name;/*** 字段名称超过10个字符时,生成shp字段定义会出错*/@GeoJSON(field = "the_pro", type = Double.class)private double the_properties;public int getId() {return id;}public void setId(int id) {this.id = id;}public Point getLocation() {return location;}public void setLocation(Point location) {this.location = location;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getThe_properties() {return the_properties;}public void setThe_properties(double the_properties) {this.the_properties = the_properties;}
}

测试类

 /*** 读取shp文件,封装成空间对象* @throws Exception*/@Testpublic void testBouFromShp() throws Exception {String shpFile = "src\\main\\resources\\shp\\jiedao.shp";//工具方法一readShp,从shp文件读取,封装成空间对象List<GovBou> objs = shapeTools.readShp(shpFile, feature -> {GovBou bou = new GovBou();//shp文件的所有属性:the_geom, FNAME, FEATUREGUI, SHAPE_Leng, SHAPE_AreaObject geom = feature.getAttribute("the_geom");String name = feature.getAttribute("FNAME").toString();if(null != geom && geom instanceof MultiPolygon){bou.setBou((MultiPolygon) geom);}bou.setName(name);bou.setId(String.valueOf(System.currentTimeMillis()));return bou;});System.out.println("读取个数:" + objs.size());//工具方法二toGeoJSON,空间对象转换层GeoJSON格式字符串,通过注解自动生成String geojson = shapeTools.toGeoJSON(objs.get(0));System.out.println(geojson);}/*** 从shp文件获取数据到对象也可以调用这个方法,利用类上的GeoJSON注解进行属性对应*/@Testpublic void testBouFromShp2() throws Exception {String shpFile = "src\\main\\resources\\shp\\jiedao.shp";List<GovBou> govBouList = shapeTools.readShp(shpFile, shapeTools.useDefaultBuilder(GovBou.class));System.out.println("获取到Bou对象总数:"+govBouList.size());}/*** 工具方法五:空间对象保存成shp文件* @throws Exception*/@Testpublic void testWriteToShp2() throws Exception {WKTReader reader = new WKTReader();List<Poi> poiList = new ArrayList<>();Poi p1 = new Poi();p1.setId(1);p1.setName("aaa");p1.setThe_properties(234.567);p1.setLocation((Point)reader.read("POINT(120.12 30.421)"));poiList.add(p1);String filePath = "src\\main\\resources\\shp\\poi1.shp";boolean result = shapeTools.writeToShpFromGeoJSON(poiList, filePath);System.out.println(result ? "保存shp文件成功" :  "失败");//验证一下从生成的shp文件读取List<Poi> pois = shapeTools.readShp(filePath, shapeTools.useDefaultBuilder(Poi.class));if(pois.size() > 0){Poi p = pois.get(0);System.out.println(shapeTools.toGeoJSON(p));}}

3.coding 地址:coding

https://e.coding.net/bitree/geotools-dev.git

JAVA 读取shp数据,shp导入,导出工具相关推荐

  1. Shp数据批量导入Postgresql工具的原理和设计

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 在制作整体的开源工具箱产品中,数据入库是一个重要的环节.虽然 ...

  2. Java Excel表格数据的导入导出

    ExcelJava EazyPoi 代码地址: GitEE:https://gitee.com/yixun0623/EasyPoi/tree/master 官方文档地址 老地址:http://easy ...

  3. Java操作百万数据量Excel导入导出工具类(程序代码教程)

    Java操作百万数据量Excel导入导出工具类(程序代码教程): # 功能实现1.自定义导入数据格式,支持配置时间.小数点类型(支持单/多sheet)(2种方式:本地文件路径导入(只支持xls.xls ...

  4. 数据如何导入oracle数据库,如何用Oracle导入导出工具来实现Oracle数据库移植?

    Oracle数据库作为目前市场的主流数据库之一,许多应用都在其上进行开发,由于Oracle数据库更新换代的原因和不同的应用程序提供商,可能会造成在一个单位的应用中存在Oracle的几种版本,如Orac ...

  5. PostGis基本操作-新建空间数据库与shp数据的导入

    场景 PostGresSQL简介与Windows上的安装教程: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/113981563 ...

  6. oracle数据库读取工具,用Oracle导入导出工具实现Oracle数据库移植

    用Oracle导入导出工具实现Oracle数据库移植.很不错的方法,建议使用![@more@] 用Oracle导入导出工具实现Oracle数据库移植 Oracle数据库作为目前市场的主流数据库之一,许 ...

  7. ETL数据导入/导出工具 HData

    HData是一个异构的ETL数据导入/导出工具,致力于使用一个工具解决不同数据源(JDBC.Hive.HDFS.HBase.MongoDB.FTP.Http.CSV.Excel.Kafka等)之间数据 ...

  8. java Excel导入导出工具类 及使用demo

    java Excel导入导出工具类 及使用demo 前言:相信进来的都是想尽快解决问题的,话不多说,按照以下步骤来,可以操作导出excel到本地,导入同理,自行学习.步骤一:直接复制以下excel工具 ...

  9. udaldump数据导入导出工具使用

    udal数据库数据导入导出工具udaldump使用示例 帮助查看 ./start -h usage: 参数说明 -bufSize,–bufSize 导入文件时使用的缓冲区大小(单位:Byte) -c, ...

  10. mysql navicat导入bcp_SQL Server数据导入导出工具BCP详解

    BCP是SQL Server中负责导入导出数据的一个命令行工具,它是基于DB-Library的,并且能以并行的方式高效地导入导出大批量的数据.BCP可以将数据库的表或视图直接导出,也能通过SELECT ...

最新文章

  1. Matlab数据的可视化 -- 饼图
  2. 「十项全能」图神经网络能干嘛?
  3. Python面向对象基础:设置对象属性
  4. python(40):利用utf-8编码判断中文英文字符
  5. 【Notepad++】Notepad++ 插件 for js 各种插件全介绍
  6. STM32开发 -- Ublox GPS之设置PUBX
  7. 网页中的宽高度(网页视口宽高度,网页滚动宽高度,网页宽高度........)
  8. UIview需要知道的一些事情:setNeedsDisplay、setNeedsLayout
  9. 括号匹配+Java栈
  10. 如何写登录的记住账号
  11. redis-Set集合操作SADD,SMEMBERS,scard,srem
  12. Spring-MetadataReader接口
  13. 虚拟机 django 端口无法连接
  14. PySide QtCore.Signal帮助手册
  15. java treeset 降序,Java TreeSet,Collections使用
  16. POJ1159 Palindrome(dp)
  17. excel怎么设置自动计算_Excel工作进度表,自动甘特进度图,函数计算简单实用...
  18. 台式计算机拆装过程和注意事项,怎么组装台式电脑 台式电脑组装注意事项
  19. 【云原生】K8S包管理(helm)
  20. 计算机ppt音乐,ppt背景音乐_适合ppt播放的轻音乐

热门文章

  1. 桌面下雪软件测试工程师,Xsnow - 在Ubuntu 18.04及更高版本的桌面上下雪
  2. 2020年20种最佳Android应用程序模板
  3. 优质的易语言 E语言游戏源码素材推荐,不容错过
  4. python程序设计课程设计二级减速器_二级减速器课程设计
  5. ALOS 12.5米精度DEM数据下载与处理
  6. Python学习手册-笔记2
  7. 我的高拍仪自动阅卷系统
  8. 大数据Hadoop原理:大数据Hadoop技术原理简介
  9. SQL最全基础教程(有本事别看啊!)
  10. Android 程序员计算器 开发记录-Git版本控制初步接触