代碼github地址:
實現效果
具體實現效果可參照我的前一篇文章:
https://blog.csdn.net/qq_42779423/article/details/106885322
程序具體實現
數據存儲與操作方式:
-
將湖北市域圖形數據存儲在shp文件中,通過加載shp按鈕進行載入;
-
選擇網易的疫情實時動態播報平臺作爲數據源,其地址如下:
https://wp.m.163.com/163/page/news/virus_report/index.html?nw=1&anw=1
通過爬蟲請求獲取數據(從1.1日至5.31日),經過數據清洗後保存爲csv文件;
-
在具有公網ip地址的 windows server 上搭建mysql數據庫,將確診人數數據存入數據庫中,連接數據庫獲取確診數據信息;可以便於後續在服務器上繼續更新數據;
-
創建了DAO層,將數據庫的增刪改查等操作封裝在工具類中,和具體程序業務邏輯分隔開來,其中包含了三個類:
-
SqlHelper
:創建數據庫連接、執行數據庫命令、 創建MySqlDataReader對象:其中定義的接口:
public MySqlConnection getMySqlCon(); public int getMySqlCom(string M_str_sqlstr, params MySqlParameter[] parameters); public DataTable getMySqlRead(string M_str_sqlstr, params MySqlParameter[] parameters);
-
sqlDataFormat
:進行數據格式的修改:其中定義的接口:
public static string dataFormat(string str);
-
OperateDatabase
:定義了數據庫增加、刪除、修改、查找的接口;其中定義的接口:
public static int Insert(string TableName,ArrayList arr); public static DataTable select(string TableName, ArrayList arr); public static int Update(string TableName, ArrayList arr,ArrayList arr_where); public static int Delete(string TableName, ArrayList arr_where);
-
程序模塊設計與文件組織:
程序可以分爲以下幾個模塊:
-
輔助類:
包含和數據庫操作相關的DAO層、圖例附加屬性定義和日誌模塊;除了上述描述的數據操作類以外,還有:
- EnumMapSurroundType:圖例附加屬性定義類
- Log: 日誌模塊類
-
地圖操作相關:
主要包含地圖操作(平移、縮放),地圖渲染,以及地圖導出等功能;
- Form1:地圖展示和操作相關的實現;
- GisClass:包含了打開MXD文件、shp文件,以及地圖渲染的一些輔助函數;
-
屬性操作相關
包含在地圖上進行空間查詢屬性、在屬性表中進行屬性編輯等;
- Form1:屬性表編輯和展示等操作
- SeletionForm:進行屬性查詢
- AddForm:添加數據
-
疫情數據統計模塊:
包含對疫情的統計圖表生成操作;
- StaticsForm類
從界面美觀的角度考慮,我們採用了DevExpress進行開發;DevExpress是一個比較有名的界面控件套件,提供了一系列的界面控件套件的DotNet界面控件。
窗口:
- 主窗體類爲
Form1.cs
; - 進行屬性查詢選擇窗體類爲
SeletionForm.cs
- 統計圖表類爲
StaticsForm.cs
- 添加數據類爲
AddForm.cs
主要功能實現流程與方法
-
地圖展示和常規地圖操作:
- 採用ArcEngine的mapControl控件進行地圖展示:
- 採用ArcEngine的ToolbarControl控件完成常規的地圖操作,如放大、縮小、平移、全圖;
- 加載shp/mxd文件:
打開mxd文件:
private void openMxd_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { String MxdPath=GisClass.OpenMxd(); axMapControl1.LoadMxFile(MxdPath); } public static string OpenMxd() { string MxdPath = ""; OpenFileDialog OpenMXD = new OpenFileDialog(); OpenMXD.Title = "打開地圖"; OpenMXD.InitialDirectory = "E:"; OpenMXD.Filter = "Map Documents (*.mxd)|*.mxd"; if (OpenMXD.ShowDialog() == DialogResult.OK) { MxdPath = OpenMXD.FileName; } return MxdPath; }
打開shp文件:
public static string[] OpenShapeFile() { string[] ShpFile = new string[2]; OpenFileDialog OpenShpFile = new OpenFileDialog(); OpenShpFile.Title = "打開Shape文件"; OpenShpFile.InitialDirectory = "E:"; OpenShpFile.Filter = "Shape文件(*.shp)|*.shp"; if (OpenShpFile.ShowDialog() == DialogResult.OK) { string ShapPath = OpenShpFile.FileName; //利用"\\"將文件路徑分成兩部分 int Position = ShapPath.LastIndexOf("\\"); string FilePath = ShapPath.Substring(0, Position); string ShpName = ShapPath.Substring(Position + 1); ShpFile[0] = FilePath; ShpFile[1] = ShpName; } else { return null; } return ShpFile; }
-
每日疫情分佈顯示:
- 通過打開shp文件按鈕加載
市域.shp
,再遍歷圖層獲取湖北市域空間數據;如未加載,系統會報錯如下:
//遍歷,尋找市域圖層 for (int i = 0; i < this.axMapControl1.Map.LayerCount; i++) { ILayer layer1 = this.axMapControl1.Map.get_Layer(i); if (layer1.Name == "市域") { layer = layer1 as IFeatureLayer; break; } } if (layer == null) { MessageBox.Show("請打開市域圖層"); return; }
- 點擊每日疫情按鈕,首先獲取圖層的相應字段,然後根據選擇的日期在數據庫中進行查詢,獲取疫情數據;
//獲取圖層字段,沒有則添加一個num字段 IFeatureClass featureClass = layer.FeatureClass; int isExist=featureClass.FindField("num"); if (isExist == -1) { //添加一個字段 IFields pFields = featureClass.Fields; IFieldsEdit pFieldsEdit = pFields as IFieldsEdit; IField fld = new FieldClass(); IFieldEdit2 fldE = fld as IFieldEdit2; fldE.Name_2 = "num"; fldE.AliasName_2 = "數量"; fldE.Type_2 = esriFieldType.esriFieldTypeSingle; featureClass.AddField(fld); } //給字段賦值 IFeatureCursor pFtCursor = featureClass.Search(null, false); IFeature pFt = pFtCursor.NextFeature(); int index1 = pFt.Fields.FindField("num"); IDataset dataset = (IDataset)featureClass; IWorkspace workspace = dataset.Workspace; IWorkspaceEdit workspaceEdit = (IWorkspaceEdit)workspace; workspaceEdit.StartEditing(true); workspaceEdit.StartEditOperation(); while (pFt != null) { int index = pFt.Fields.FindField("code"); String code = pFt.get_Value(index).ToString(); DataRow[] drs=dt.Select("CODE=" + code); DataTable dtNew = dt.Clone(); for (int i = 0; i < drs.Length; i++) { dtNew.ImportRow(drs[i]); } String num = dtNew.Rows[0]["AllConfiemed"].ToString(); if (num == "") { num = "0"; } pFt.set_Value(index1, Convert.ToInt32(num)); pFt.Store(); pFt = pFtCursor.NextFeature(); }
- 根據獲取的數據對圖層進行渲染
GisClass.ClassRender(this.axMapControl1.ActiveView, layer, 6, "num");
- 通過打開shp文件按鈕加載
-
空間查詢操作:
- 通過點擊圖形按鈕,繪製多邊形、圓、矩形等;
如繪製多邊形:先設置繪製類型爲多邊形,再創建一個多邊形元素,設置相應屬性,在pGraphicsContainer中添加該多邊形;然後鼠標點擊時追蹤多邊形,並局部刷新map
private void drawPolygon_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { this.type = 1; IPolygonElement polygonElement = new PolygonElementClass(); pElement = polygonElement as IElement; ISimpleFillSymbol simpleFill = new SimpleFillSymbolClass(); simpleFill.Style = esriSimpleFillStyle.esriSFSNull; simpleFill.Color = GisClass.GetRgbColor(255,0,0); //設置邊線顏色 ILineSymbol lineSymbol = new SimpleLineSymbol(); lineSymbol.Color = GisClass.GetRgbColor(255, 0, 0); IFillShapeElement shapeEle = pElement as IFillShapeElement; simpleFill.Outline = lineSymbol; shapeEle.Symbol = simpleFill; pGraphicsContainer.AddElement(pElement, 0); } private void axMapControl1_OnMouseDown(object sender, IMapControlEvents2_OnMouseDownEvent e{ ....... if (this.type == 1) { IGeometry Polygon = axMapControl1.TrackPolygon(); pElement.Geometry = Polygon; axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewBackground, null, null); } ...... }
- 通過點擊查詢,對所選範圍執行空間查詢操作,對地圖上查詢到的部分進行高亮顯示;
private void query_btn_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { ArrayList arr = new ArrayList(); DataTable dt = OperateDatabase.select("data", arr); this.gridControl1.DataSource = dt; this.tabControl2.SelectedIndex = 1; }
- 點擊進行屬性查詢,打開屬性表;
private void shapeQuery_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { axMapControl1.Map.ClearSelection(); IGraphicsContainer graphicsContainer = axMapControl1.Map as IGraphicsContainer; graphicsContainer.Reset(); IElement element = graphicsContainer.Next(); //獲取圖形幾何信息 if (element == null) { MessageBox.Show("請在工具欄選擇繪製矩形,多邊形,或者圓"); return; } IGeometry geometry = element.Geometry; axMapControl1.Map.SelectByShape(geometry,null,false); //進行部分刷新顯示最新要素 axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection,null,axMapControl1.ActiveView.Extent); }
-
屬性數據編輯:
- 在屬性數據的頁面中,可以點擊查詢、增加、刪除等按鈕進行屬性數據的編輯;
修改單元格內容:
//獲取修改的單元格 string CellValue = this.gridView1.GetFocusedValue().ToString(); //獲取單元格的列名 string ColumnName = this.gridView1.FocusedColumn.FieldName; //獲取所在列的id DataRow dr = this.gridView1.GetDataRow(e.RowHandle); string id = dr["id"].ToString(); //修改 ArrayList arr = new ArrayList(); if (ColumnName == "name" || ColumnName == "YMD") { arr.Add(ColumnName + ":'" + CellValue + "'"); } else { arr.Add(ColumnName + ":" + CellValue); } ArrayList arr_where = new ArrayList(); arr_where.Add("id:" + id); int result = OperateDatabase.Update("data", arr, arr_where); if (result == 0) { MessageBox.Show("該值修改失敗"); }
添加數據:
private void add_btn_Click(object sender, EventArgs e) { ArrayList arr = new ArrayList(); arr.Add("code:"+this.textBox_code.Text); arr.Add("name:'" + this.textBox_name.Text+"'"); arr.Add("YMD:'" + this.date_edit.Text+"'"); arr.Add("AllConfiemed:" + this.spinEdit_AllConfiemed.Text); arr.Add("CurConfirmeed:" + this.spinEdit_CurConfirmeed.Text); arr.Add("Cured:" + this.spinEdit_Cured.Text); arr.Add("Death:" + this.spinEdit_Death.Text); int result = OperateDatabase.Insert("data",arr); if (result == 1) { MessageBox.Show("添加成功"); return; }else { MessageBox.Show("添加失敗"); return; } }
屬性查詢結果:
在屬性查詢結果中是以樹的方式展示不同圖層的查詢結果:
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e) { this.gridView1.Columns.Clear(); currentLayer = e.Node.Tag as IFeatureLayer; if (currentLayer == null) { return; } IFeatureSelection featureSelection = currentLayer as IFeatureSelection; //獲取選中得要素幾何 ISelectionSet selectionSet = featureSelection.SelectionSet; //獲取字段 IFields fields = currentLayer.FeatureClass.Fields; DataTable dt = new DataTable(); for (int i = 0; i < fields.FieldCount; i++) { dt.Columns.Add(fields.get_Field(i).Name); } //獲取整個數據集 ICursor cursor; selectionSet.Search(null,false,out cursor); //獲取每個要素 IFeatureCursor featureCursor = cursor as IFeatureCursor; //遍歷 IFeature feature = featureCursor.NextFeature(); String[] strs; while (feature != null) { strs = new String[fields.FieldCount]; for (int i = 0; i < fields.FieldCount; i++) { strs[i] = feature.get_Value(i).ToString(); } dt.Rows.Add(strs); feature = featureCursor.NextFeature(); } this.gridControl1.DataSource = dt; }
-
疫情統計:
- 在主頁面上點擊疫情統計,可顯示查詢窗口,其中可完成對於疫情統計圖表的生成和查看;
private void statics_btn_Click(object sender, EventArgs e) { //查詢起始日期的數字 if (this.dateEdit_start.Text == "" || this.dateEdit_target.Text == "") { MessageBox.Show("請填寫起止日期"); return; } ArrayList arr1 = new ArrayList(); arr1.Add("YMD:'" + this.dateEdit_start.Text + "'"); DataTable dt1 = OperateDatabase.select("data",arr1); ArrayList arr2 = new ArrayList(); arr1.Add("YMD:'" + this.dateEdit_target.Text + "'"); DataTable dt2 = OperateDatabase.select("data", arr1); Series s1 = this.chartControl1.Series[0]; s1.DataSource = dt1; s1.ArgumentDataMember = "name"; s1.ValueDataMembers[0] = "CurConfirmeed"; }
-
軌跡分析:
- 通過日期框進行日期區間的選擇;
- 軌跡數據已存放在數據庫中,通過sql查詢載入軌跡數據:
- 進行軌跡查詢:
- 繪製軌跡:
if (this.start_time.EditValue == "" || this.end_time.EditValue == "") { MessageBox.Show("請選擇起止日期"); return; } SqlHelper help = new SqlHelper(); String sql = "select * from route where tm between '" + this.start_time.EditValue + "' and '" + this.end_time.EditValue+"'"; DataTable dt = help.getMySqlRead(sql); ISimpleMarkerSymbol simpleMarkerSymbol = new SimpleMarkerSymbol(); simpleMarkerSymbol.Style = esriSimpleMarkerStyle.esriSMSCircle; IColor color = GisClass.GetRgbColor(0,255,0); simpleMarkerSymbol.Color = color; ILineElement lineElement = new LineElementClass(); IElement ele1 = lineElement as IElement; ISegment pSegment; ILine pLine=null; object o = Type.Missing; ISegmentCollection pPath = new PathClass(); for (int i = 0; i < dt.Rows.Count; i++) { IMarkerElement markerEle = new MarkerElementClass(); IElement ele=markerEle as IElement; IPoint point = new PointClass(); markerEle.Symbol = simpleMarkerSymbol; point.PutCoords(Double.Parse(dt.Rows[i]["x"].ToString()),Double.Parse(dt.Rows[i]["y"].ToString())); ele.Geometry = point; pGraphicsContainer.AddElement(ele,0); //逐段添加線 if (i > 0 && i < dt.Rows.Count) { IPoint point1 = new PointClass(); point1.PutCoords(Double.Parse(dt.Rows[i-1]["x"].ToString()), Double.Parse(dt.Rows[i-1]["y"].ToString())); pLine = new LineClass(); pLine.PutCoords(point1, point); pSegment = pLine as ISegment; pPath.AddSegment(pSegment, ref o, ref o); } axMapControl1.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewBackground, null, null); } IGeometryCollection pPolyline = new PolylineClass(); pPolyline.AddGeometry(pPath as IGeometry, ref o, ref o); IPolyline polyline = pPolyline as IPolyline; //獲取範圍 IEnvelope ev = polyline.Envelope; this.axMapControl1.ActiveView.Extent = ev; ele1.Geometry = pPolyline as IPolyline; pGraphicsContainer.AddElement(ele1, 0);
-
每日疫情圖輸出:
- 添加圖例:可爲地圖添加指北針、比例尺等;
添加指北針:
void addNorthArrow(IPageLayout pPageLayout, IEnvelope pEnv, IActiveView pActiveView) { if (pPageLayout == null || pActiveView == null) { return; } ESRI.ArcGIS.esriSystem.IUID uid = new ESRI.ArcGIS.esriSystem.UIDClass(); uid.Value = "esriCarto.MarkerNorthArrow"; ESRI.ArcGIS.Carto.IGraphicsContainer graphicsContainer = pPageLayout as ESRI.ArcGIS.Carto.IGraphicsContainer; ESRI.ArcGIS.Carto.IActiveView activeView = pPageLayout as ESRI.ArcGIS.Carto.IActiveView; ESRI.ArcGIS.Carto.IFrameElement frameElement = graphicsContainer.FindFrame(pActiveView.FocusMap); ESRI.ArcGIS.Carto.IMapFrame mapFrame = frameElement as ESRI.ArcGIS.Carto.IMapFrame; // Dynamic Cast ESRI.ArcGIS.Carto.IMapSurroundFrame mapSurroundFrame = mapFrame.CreateSurroundFrame(uid as ESRI.ArcGIS.esriSystem.UID, null); // Dynamic Cast ESRI.ArcGIS.Carto.IElement element = mapSurroundFrame as ESRI.ArcGIS.Carto.IElement; // Dynamic Cast element.Geometry = pEnv; element.Activate(activeView.ScreenDisplay); graphicsContainer.AddElement(element, 0); ESRI.ArcGIS.Carto.IMapSurround mapSurround = mapSurroundFrame.MapSurround; // Change out the default north arrow ESRI.ArcGIS.Carto.IMarkerNorthArrow markerNorthArrow = mapSurround as ESRI.ArcGIS.Carto.IMarkerNorthArrow; // Dynamic Cast ESRI.ArcGIS.Display.IMarkerSymbol markerSymbol = markerNorthArrow.MarkerSymbol; ESRI.ArcGIS.Display.ICharacterMarkerSymbol characterMarkerSymbol = markerSymbol as ESRI.ArcGIS.Display.ICharacterMarkerSymbol; // Dynamic Cast characterMarkerSymbol.CharacterIndex = 200; // change the symbol for the North Arrow markerNorthArrow.MarkerSymbol = characterMarkerSymbol; }
添加比例尺:
public void makeScaleBar(IActiveView pActiveView, IPageLayout pPageLayout, IEnvelope pEnv) { IGraphicsContainer container = pPageLayout as IGraphicsContainer; // 獲得MapFrame IFrameElement frameElement = container.FindFrame(pActiveView.FocusMap); IMapFrame mapFrame = frameElement as IMapFrame; //根據MapSurround的uid,創建相應的MapSurroundFrame和MapSurround UID uid = new UIDClass(); uid.Value = "esriCarto.AlternatingScaleBar"; IMapSurroundFrame mapSurroundFrame = mapFrame.CreateSurroundFrame(uid, null); //設置MapSurroundFrame中比例尺的樣式 IMapSurround mapSurround = mapSurroundFrame.MapSurround; IScaleBar markerScaleBar = ((IScaleBar)mapSurround); markerScaleBar.LabelPosition = esriVertPosEnum.esriBelow; markerScaleBar.UseMapSettings(); //QI,確定mapSurroundFrame的位置 IElement element = mapSurroundFrame as IElement; element.Geometry = pEnv; //使用IGraphicsContainer接口添加顯示 container.AddElement(element, 0); pActiveView.Refresh(); } #endregion
- 點擊輸出按鈕,可將疫情圖輸出爲多種格式:
如導出爲圖片:
private void ExportMapToImage() { try { SaveFileDialog pSaveDialog = new SaveFileDialog(); pSaveDialog.FileName = ""; pSaveDialog.Filter = "JPG圖片(*.JPG)|*.jpg|tif圖片(*.tif)|*.tif|PDF文檔(*.PDF)|*.pdf"; if (pSaveDialog.ShowDialog() == DialogResult.OK) { double iScreenDispalyResolution =this.axPageLayoutControl1.ActiveView.ScreenDisplay.DisplayTransformation.Resolution;// 獲取屏幕分辨率的值 IExporter pExporter = null; if (pSaveDialog.FilterIndex == 1) { pExporter = new JpegExporterClass(); } else if (pSaveDialog.FilterIndex == 2) { pExporter = new TiffExporterClass(); } else if (pSaveDialog.FilterIndex == 3) { pExporter = new PDFExporterClass(); } pExporter.ExportFileName = pSaveDialog.FileName; pExporter.Resolution = (short)iScreenDispalyResolution; //分辨率 tagRECT deviceRect = this.axPageLayoutControl1.ActiveView.ScreenDisplay.DisplayTransformation.get_DeviceFrame(); IEnvelope pDeviceEnvelope = new EnvelopeClass(); pDeviceEnvelope.PutCoords(deviceRect.left, deviceRect.bottom, deviceRect.right, deviceRect.top); pExporter.PixelBounds = pDeviceEnvelope; // 輸出圖片的範圍 ITrackCancel pCancle = new CancelTrackerClass();//可用ESC鍵取消操作 this.axPageLayoutControl1.ActiveView.Output(pExporter.StartExporting(), pExporter.Resolution, ref deviceRect, this.axPageLayoutControl1.ActiveView.Extent, pCancle); Application.DoEvents(); pExporter.FinishExporting(); } } catch (Exception Err) { MessageBox.Show(Err.Message, "輸出圖片", MessageBoxButtons.OK, MessageBoxIcon.Information); } }
除了導出爲圖片之外,支持多種其他格式,如pdf、jpg等