老师让我实现如题的功能,我对着ArcGIS js api找了半天,没有发现该方法接口,找了很多资料,前后问了三个前辈。
第一个前辈说用GP服务,我在ArcMap的工具箱里找到convert to layerfile这个功能,试过之后。发现该方法的不足之处在于①只能输出整张图层,我希望能选择任意的部分输出②发布成GP服务后,只能控制输入参数(输入的图层),而输出参数(输出的文件名)不能控制。该方法GG。
第二个前辈说,将查询出来的实体写进gdb里。我就找啊找,找啊找,没找到api有这个功能。
后来自己分析查询出来的graphics,分析其dom组成部分,意外的发现,存在attributes和gepmetry两个部分,又发现ArcMap的工具箱里存在JSON TO Feature和Feature TO JSON两个功能,似乎可以搞点事情出来。此时,正好遇到第三个前辈,我跟他提出了我的问题以及自己的想法,认可了我的观点,并指出不要用现成的工具箱发布GP服务来转,会有问题,用Arcengine二次开发,自己解析。我可是想偷懒的人,放着GP服务不用是不是有点可惜,试了下,ArcMap将点转出为JSON,再将JSON转为点没问题,而转线和面时,问题就出来了,能转出JSON,而不能再将JSON转回去。前辈诚不欺我。
好了,不说废话了,下面介绍我的解决方法,分四步:①graphics转JSON ②JSON转Geometry ③Geometry转Feature ④Feature输出为Shapfile
一、graphics转JSON
我的graphics只有三种简单类型,point,polyline,polygon,拼接字符串的时候要细心,下面是我的代码
1 //created by YS 20171218 2 3 //判断属性值是字符串?整型?浮点型? 4 //返回对应于esri的值类型 5 function checkValueType(value) { 6 if (/^-?\d*\.\d+$/.test(value))//是否浮点型 7 return "esriFieldTypeDouble"; 8 else if (/^\d+$/.test(value))//是否整型 9 return "esriFieldTypeInteger"; 10 else 11 return "esriFieldTypeString";//字符串型 12 13 } 14 //******参数类型 graphic集合,必须是单一的点或线或面类型 15 //******输出类型 json 16 function convertToJson(graphics) { 17 var attributes = graphics[0].attributes;//取得属性 18 //取得属性名 19 var attributes_name = []; 20 for (var key in attributes) { 21 //排除attributes.SHAPE 22 if (key.toUpperCase() == "SHAPE") 23 continue; 24 attributes_name.push(key); 25 } 26 27 //判断geometryType类型 28 var geometryType = ""; 29 switch (graphics[0].geometry.type) { 30 case "polygon": 31 geometryType = "esriGeometryPolygon"; 32 break; 33 case "polyline": 34 geometryType = "esriGeometryPolyline"; 35 break; 36 case "point": 37 geometryType = "esriGeometryPoint"; 38 break; 39 default: break; 40 } 41 // 42 //获取spatialReference 43 var spatialReference = $.toJSON(graphics[0].geometry.spatialReference); 44 45 //判断属性值类型 46 var attributes_valueType = []; 47 for (var j = 0; j < attributes_name.length; j++) { 48 attributes_valueType[j] = checkValueType(graphics[0].attributes[attributes_name[j]]); 49 } 50 51 //准备属性名和属性类型 52 var fields = ""; 53 54 for (var i = 0; i < graphics.length; i++) { 55 var field = ""; 56 for (var j = 0; j < attributes_name.length; j++) { 57 58 if (attributes_valueType[j] == "esriFieldTypeString") {//字符串型字段比其他类型多一个长度属性 59 var obj = new Object(); 60 obj.name = attributes_name[j]; 61 obj.type = attributes_valueType[j]; 62 obj.alias = attributes_name[j]; 63 obj.length = "50"; 64 field += $.toJSON(obj) + ","; 65 } else { 66 var obj = new Object(); 67 obj.name = attributes_name[j]; 68 obj.type = attributes_valueType[j]; 69 obj.alias = attributes_name[j]; 70 field += $.toJSON(obj) + ","; 71 } 72 } 73 } 74 //去掉最后一个逗号 75 field = field.substring(0, field.length - 1); 76 //拼接fields 77 //***********arcmap将要素转json时,输出数据中有FID********************************************* 78 //***********该json通过ArcEngine转为shapfile时,不需要拼接FID*********************************** 79 //var fields = "{\"name\":\"FID\",\"type\":\"esriFieldTypeOID\",\"alias\":\"FID\"}," + field; 80 var fields = field; 81 82 //准备features(attributes+geometry) 83 var features = ""; 84 var attributes = []; 85 var geometry = []; 86 //*******比下面注释掉的程序省掉多次循环判断,降低时间复杂度***** 87 if (geometryType == "esriGeometryPolygon") { 88 for (var i = 0; i < graphics.length; i++) { 89 //删除“SHAPE”属性 90 var attr = graphics[i].attributes; 91 //SHAPE大小写不确定 92 delete attr.SHAPE; 93 delete attr.Shape; 94 attributes[i] = $.toJSON(attr); 95 //组织面的geometry 96 geometry[i] = $.toJSON(graphics[i].geometry.rings); 97 features += "{\"attributes\":" + attributes[i] + ",\"geometry\":{\"rings\":" + geometry[i] + "}},"; 98 } 99 } else if (geometryType == "esriGeometryPolyline") { 100 for (var i = 0; i < graphics.length; i++) { 101 //删除“SHAPE”属性 102 var attr = graphics[i].attributes; 103 //SHAPE大小写不确定 104 delete attr.SHAPE; 105 delete attr.Shape; 106 attributes[i] = $.toJSON(attr); 107 //组织线的geometry 108 geometry[i] = $.toJSON(graphics[i].geometry.paths); 109 //如果需要将得到的 线json转成geoJson 110 //path转json后,去掉最外层的中括号[] 111 //geometry[i] = geometry[i].substring(1, geometry[i].length - 1); 112 113 features += "{\"attributes\":" + attributes[i] + ",\"geometry\":{\"paths\":" + geometry[i] + "}},"; 114 } 115 } else { 116 for (var i = 0; i < graphics.length; i++) { 117 //删除“SHAPE”属性 118 var attr = graphics[i].attributes; 119 //SHAPE大小写不确定 120 delete attr.SHAPE; 121 delete attr.Shape; 122 attributes[i] = $.toJSON(attr); 123 //组织点的geometry 124 var geo = new Object(); 125 geo.x = graphics[i].geometry.x; 126 geo.y = graphics[i].geometry.y; 127 var geo = $.toJSON(geo); 128 features += "{\"attributes\":" + attributes[i] + ",\"geometry\":" + geo + "},"; 129 } 130 } 131 158 159 //去掉最后一个逗号 160 features = features.substring(0, features.length - 1); 161 163 164 //拼接fieldAliases 165 var fieldAliases = ""; 166 for (var i = 0; i < attributes_name.length; i++) { 167 fieldAliases += "\"" + attributes_name[i] + "\":\"" + attributes_name[i] + "\","; 168 } 169 //去掉最后一个逗号 170 fieldAliases = fieldAliases.substring(0, fieldAliases.length - 1); 171 172 //拼接地图实体Json 173 174 //var Json = "{\"displayFieldName\":\"\",\"fieldAliases\":{\"FID\":\"FID\"," + fieldAliases + "},\"geometryType\":\"" + geometryType + "\",\"spatialReference\":" + spatialReference + ",\"fields\":[" + fields + "],\"features\":[" + features + "]}"; 175 var Json = "{\"displayFieldName\":\"\",\"fieldAliases\":{" + fieldAliases + "},\"geometryType\":\"" + geometryType + "\",\"spatialReference\":" + spatialReference + ",\"fields\":[" + fields + "],\"features\":[" + features + "]}"; 176 177 178 //console.log(Json); 179 return Json; 180 }
二、JSON转Geometry
接下来的部分就属于ArcEngine二次开发了,已经有一年没碰过它了,捡起来心真累。
ArcEngine提供了JSONConverterGeometryClass接口,用于geometry字符串转geometry对象
1 class jsonToGeo 2 { 3 /// <summary> 4 /// 将json字符串转成动态对象 5 /// </summary> 6 /// <param name="json"></param> 7 /// <returns></returns> 8 public dynamic convert(string json) 9 { 10 var DynamicObject = JsonConvert.DeserializeObject<dynamic>(json); 11 return DynamicObject; 12 13 } 14 15 /// <summary> 16 /// JSON字符串转成IGeometry 17 /// </summary> 18 public ESRI.ArcGIS.Geometry.IGeometry GeometryFromJsonString(string strJson, ESRI.ArcGIS.Geometry.esriGeometryType type) 19 { 20 return GeometryFromJsonString(strJson, type, false, false); 21 } 22 23 /// <summary> 24 /// JSON字符串转成IGeometry 25 /// </summary> 26 public ESRI.ArcGIS.Geometry.IGeometry GeometryFromJsonString(string strJson, ESRI.ArcGIS.Geometry.esriGeometryType type, 27 bool bHasZ, bool bHasM) 28 { 29 ESRI.ArcGIS.esriSystem.IJSONReader jsonReader = new ESRI.ArcGIS.esriSystem.JSONReaderClass(); 30 jsonReader.ReadFromString(strJson); 31 32 ESRI.ArcGIS.Geometry.JSONConverterGeometryClass jsonCon = new ESRI.ArcGIS.Geometry.JSONConverterGeometryClass(); 33 return jsonCon.ReadGeometry(jsonReader, type, bHasZ, bHasM); 34 } 35 /// <summary> 36 /// IGeometry转成JSON字符串 37 /// </summary> 38 public string GeometryToJsonString(ESRI.ArcGIS.Geometry.IGeometry geometry) 39 { 40 ESRI.ArcGIS.esriSystem.IJSONWriter jsonWriter = new ESRI.ArcGIS.esriSystem.JSONWriterClass(); 41 jsonWriter.WriteToString(); 42 43 ESRI.ArcGIS.Geometry.JSONConverterGeometryClass jsonCon = new ESRI.ArcGIS.Geometry.JSONConverterGeometryClass(); 44 jsonCon.WriteGeometry(jsonWriter, null, geometry, false); 45 46 return Encoding.UTF8.GetString(jsonWriter.GetStringBuffer()); 47 } 48 49 }
解析JSON,先将其转成JSON对象,提取出List<IGeometry>
1 /// <summary> 2 /// 将JSON字符串转成Shapfile文件 3 /// </summary> 4 /// <param name="json">输入的Json字符串</param> 5 /// <param name="outputFileFolder">输出的文件地址</param> 6 /// <param name="ShapeName">输出的文件名(不带后缀)</param> 7 public static void JsonToShp(string json, string outputFileFolder, string ShapeName) 8 { 9 //将json字符串转成动态对象 10 jsonToGeo jTG = new jsonToGeo(); 11 var obj = jTG.convert(json); 12 13 //获取json中的features集 14 var features = obj["features"]; 15 int count = obj["features"].Count;//图形个数 16 //obj["features"][0]["geometry"]表示第一个图形的geometry 17 18 //坐标系 19 int wkid = (int)obj["spatialReference"]["wkid"]; 20 21 //创建SpatialReferenceEnvironmentClass对象 22 ISpatialReferenceFactory2 pSpaRefFactory = new SpatialReferenceEnvironmentClass(); 23 //创建地理坐标系对象 24 //IGeographicCoordinateSystem pNewGeoSys = pSpaRefFactory.CreateGeographicCoordinateSystem(gcsType);//4214代表Beijing1954 25 //创建投影坐标系 26 IProjectedCoordinateSystem pGeoSys = pSpaRefFactory.CreateProjectedCoordinateSystem(wkid);//2437 Beijing_1954_3_Degree_GK_CM_120E 27 28 //字段集合 29 var fields = obj["fields"]; 30 dynamic shpFields; 31 shpFields = fields; 32 33 34 35 //List<IPoint> points = new List<IPoint>(); 36 //List<IPolyline> polylines = new List<IPolyline>(); 37 //List<IPolygon> polygons = new List<IPolygon>(); 38 39 List<IGeometry> geometryList = new List<IGeometry>(); 40 41 GeoToFeature gtf = new GeoToFeature(); 42 43 44 string geometryType = obj["geometryType"].ToString(); 45 switch (geometryType) 46 { 47 case "esriGeometryPoint": 48 for (int i = 0; i < count; i++) 49 { 50 IPoint pointLocation = null; 51 pointLocation = jTG.GeometryFromJsonString(features[i]["geometry"].ToString(), ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPoint) as ESRI.ArcGIS.Geometry.IPoint; 52 pointLocation.SpatialReference = pGeoSys; 53 IGeometry point = pointLocation as IGeometry; 54 //points.Add(pointLocation); 55 geometryList.Add(point); 56 } 57 //创建点shp,并添加字段 58 gtf.CreateShapefile(outputFileFolder, ShapeName, esriGeometryType.esriGeometryPoint, pGeoSys, shpFields); 59 60 break; 61 case "esriGeometryPolyline": 62 for (int i = 0; i < count; i++) 63 { 64 IPolyline polylineLocation = jTG.GeometryFromJsonString(features[i]["geometry"].ToString(), ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolyline) as IPolyline; 65 polylineLocation.SpatialReference = pGeoSys; 66 IGeometry polyline = polylineLocation as IGeometry; 67 //polylines.Add(polylineLocation); 68 geometryList.Add(polyline); 69 } 70 //创建线shp,并添加字段 71 gtf.CreateShapefile(outputFileFolder, ShapeName, esriGeometryType.esriGeometryPolyline, pGeoSys, shpFields); 72 break; 73 case "esriGeometryPolygon": 74 for (int i = 0; i < count; i++) 75 { 76 IPolygon polygonLocation = jTG.GeometryFromJsonString(features[i]["geometry"].ToString(), ESRI.ArcGIS.Geometry.esriGeometryType.esriGeometryPolygon) as IPolygon; 77 polygonLocation.SpatialReference = pGeoSys; 78 IGeometry polygon = polygonLocation as IGeometry; 79 //polygons.Add(polygonLocation); 80 geometryList.Add(polygon); 81 } 82 //创建面shp,并添加字段 83 gtf.CreateShapefile(outputFileFolder, ShapeName, esriGeometryType.esriGeometryPolygon, pGeoSys, shpFields); 84 85 86 break; 87 default: break; 88 } 89 90 //添加要素 91 gtf.AddFeatureByBuffer(outputFileFolder, ShapeName, geometryList, features); 92 }
三、创建Shapfile文件
1 /// <summary> 2 /// 创建 shapfile文件并添加字段 3 /// </summary> 4 /// <param name="strShapeFolder">存放地址</param> 5 /// <param name="strShapeName">文件名</param> 6 /// <param name="geoType">几何类型</param> 7 /// <param name="geoSys">坐标系统</param> 8 /// <param name="shpFields">字段集</param> 9 public void CreateShapefile(string strShapeFolder, string strShapeName, esriGeometryType geoType, IProjectedCoordinateSystem geoSys, dynamic shpFields) 10 { 11 //打开工作空间 12 const string strShapeFieldName = "shape"; 13 IWorkspaceFactory pWSF = new ShapefileWorkspaceFactoryClass(); 14 IFeatureWorkspace pWS = (IFeatureWorkspace)pWSF.OpenFromFile(strShapeFolder, 0); 15 16 //如果已存在,那么删除 17 //IFeatureClass pFCChecker = pWS.OpenFeatureClass(strShapeName); 18 //if (pFCChecker != null) 19 //{ 20 // IDataset pds = pFCChecker as IDataset; 21 // pds.Delete(); 22 //} 23 24 //设置字段集 25 IFields pFields = new FieldsClass(); 26 IFieldsEdit pFieldsEdit = (IFieldsEdit)pFields; 27 28 //设置字段 29 IField pField = new FieldClass(); 30 IFieldEdit pFieldEdit = (IFieldEdit)pField; 31 32 //创建类型为几何类型的字段 33 pFieldEdit.Name_2 = strShapeFieldName; 34 pFieldEdit.Type_2 = esriFieldType.esriFieldTypeGeometry; 35 36 //为esriFieldTypeGeometry类型的字段创建几何定义,包括类型和空间参照 37 IGeometryDef pGeoDef = new GeometryDefClass(); //The geometry definition for the field if IsGeometry is TRUE. 38 IGeometryDefEdit pGeoDefEdit = (IGeometryDefEdit)pGeoDef; 39 //图形的类型 40 //pGeoDefEdit.GeometryType_2 = esriGeometryType.esriGeometryPoint; 41 pGeoDefEdit.GeometryType_2 = geoType; 42 //pGeoDefEdit.SpatialReference_2 = new UnknownCoordinateSystemClass(); 43 pGeoDefEdit.SpatialReference_2 = geoSys; 44 45 pFieldEdit.GeometryDef_2 = pGeoDef; 46 pFieldsEdit.AddField(pField); 47 48 49 for (int i = 0; i < shpFields.Count; i++) 50 { 51 string type = shpFields[i]["type"].ToString(); 52 esriFieldType esriType = new esriFieldType(); 53 switch (type) 54 { 55 case "esriFieldTypeDouble": 56 esriType = esriFieldType.esriFieldTypeDouble; 57 break; 58 case "esriFieldTypeInteger": 59 esriType = esriFieldType.esriFieldTypeInteger; 60 break; 61 case "esriFieldTypeString": 62 esriType = esriFieldType.esriFieldTypeString; 63 break; 64 default: break; 65 66 } 67 pField = new FieldClass(); 68 pFieldEdit = (IFieldEdit)pField; 69 //arcgis的字段名最多十个字节,十个字母或五个汉字,多出来的部分不显示,而且乱码 70 pFieldEdit.Name_2 = GetSubString(shpFields[i]["name"].ToString(), 10); 71 pFieldEdit.Type_2 = esriType; 72 pFieldsEdit.AddField(pField); 73 } 74 75 //创建shapefile 76 try 77 { 78 pWS.CreateFeatureClass(strShapeName, pFields, null, null, esriFeatureType.esriFTSimple, strShapeFieldName, ""); 79 } 80 catch (Exception e) 81 { 82 //报错的话,说明有同名文件,再次执行能自动删除 83 throw e; 84 } 85 86 94 }
这里有一坑,ArcGIS的字段名最多只支持十个字节,如果添加字段时没有注意到这一点,那么接下来你是无法插入属性的
为此,我准备了截取字符串前n个字节的方法
1 ///由于ArcGIS的字段最多十个字节,所以只能10字节以内 2 /// </summary> 3 /// <param name="origStr">原始字符串</param> 4 /// <param name="endIndex">提取前endIdex个字节</param> 5 /// <returns></returns> 6 public string GetSubString(string origStr, int endIndex) 7 { 8 if (origStr == null || origStr.Length == 0 || endIndex < 0) 9 return ""; 10 int bytesCount = System.Text.Encoding.GetEncoding("gb2312").GetByteCount(origStr); 11 if (bytesCount > endIndex) 12 { 13 int readyLength = 0; 14 int byteLength; 15 for (int i = 0; i < origStr.Length; i++) 16 { 17 byteLength = System.Text.Encoding.GetEncoding("gb2312").GetByteCount(new char[] { origStr[i] }); 18 readyLength += byteLength; 19 if (readyLength == endIndex) 20 { 21 origStr = origStr.Substring(0, i + 1); 22 break; 23 } 24 else if (readyLength > endIndex) 25 { 26 origStr = origStr.Substring(0, i); 27 break; 28 } 29 } 30 } 31 return origStr; 32 }
四、将要素写入创建的Shapfile中
1 /// <summary> 2 /// 向工作空间添加要素 3 /// </summary> 4 /// <param name="strShapeFolder">文件路径</param> 5 /// <param name="strShapeName">文件名</param> 6 /// <param name="geometryList">几何图形集</param> 7 /// <param name="objJsonFeatures">json中的feature对象</param> 8 public void AddFeatureByBuffer(string strShapeFolder, string strShapeName, List<IGeometry> geometryList, dynamic objJsonFeatures) 9 { 10 //利用缓存向shapefile文件中插入feature 11 //当数据量较大时,IFeatureClass.CreateFeatureBuffer 比 IFeatureClass.CreateFeature 快 12 13 //打开工作空间 14 IWorkspaceFactory pWSF = new ShapefileWorkspaceFactoryClass(); 15 IFeatureWorkspace pWS = (IFeatureWorkspace)pWSF.OpenFromFile(strShapeFolder, 0); 16 17 //添加要素 18 IFeatureClass featureClass = pWS.OpenFeatureClass(strShapeName); 19 20 IFeatureCursor featureCursor = featureClass.Insert(true); 21 IFeatureBuffer featureBuffer = featureClass.CreateFeatureBuffer(); 22 23 int i = 0; 24 foreach (IGeometry geometry in geometryList) 25 { 26 //给每个几何图形赋予属性值 27 28 //找到json中的attributes部分 29 var attributes = objJsonFeatures[i]["attributes"]; 30 foreach (var attribute in attributes) 31 { 32 string name = GetSubString(attribute.Name, 10); 33 string value = attribute.Value.ToString(); 34 int typeFieldIndex = featureClass.FindField(name); 35 featureBuffer.set_Value(typeFieldIndex, value); 36 } 37 // Set the feature buffer's shape and insert it. 38 featureBuffer.Shape = geometry; 39 featureCursor.InsertFeature(featureBuffer); 40 i++; 41 } 42 43 // Flush the buffer to the geodatabase. 44 featureCursor.Flush(); 45 46 47 }
综合以上程序,就能将图形输出到指定目录,并自定义文件名
以此随笔,纪念我逝去的两星期时间 T-T