C#读取shp多边形文件

本文介绍了一个C#程序,用于读取和显示SHP地理信息文件,包括地图展示、地图概况、属性表显示以及地图缩放和平移功能。程序通过二进制读取SHP文件头信息,解析多边形记录,并根据紧凑度计算填充颜色。同时,程序实现了属性表与地图多边形的双向选择,提供了一种直观的GIS数据交互方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

读取给定的shp多边形文件并显示出来,程序框架见附件中程序代码,请在程序框架内完成此程序,使其达到我们程序设计要求。

一. 设计思路:

1. Shp.cs

Shp.cs主要由两个类组成,Shp和ShpHead,思维导图如下:

图1 Shp.cs思维导图:
图1 Shp.cs思维导图

1.1. OpenShpFile()

这个函数的主要作用是打开shp文件,并将属性表数据填充到myds中。首先用二进制方式读取文件头前100字节到预定义的byte[]数组mainHead中。再将文件路径中的“.”去掉,再从右往左搜索,最后一个“\”的索引+1即为文件名(不带后缀)的索引,去掉文件名得到文件目录。最后实例化OleDbConnection和OleDbCommand对象连接dbf文件,将属性表填充到数据集中。

图2 二进制读取和获取文件名部分:
在这里插入图片描述
图3 连接dbf文件和填充数据集部分:
在这里插入图片描述

1.2. GetNextShp()

读取多边形记录时,首先读取开头的四个字节,即记录头并倒序,再转换为32位整型赋给记录号recordID;再读取4个字节,获取记录内容的长度;继续读取4个字节,为shp类型,并转换为32位整型,进行分支判断:如果shp类型为1,则读取点数据;如果shp类型为5,则读取多边形数据。
图4 GetNextShp()代码图:
在这里插入图片描述

1.3. 读取点、线、多边形记录的方法

将GetNextShp()用到的读取点、线、多边形内容抽象成方法,读取点是根据文件记录内容获取点的X,Y坐标;线和多边形则是根据其类的结构和对应的字节长度、顺序,分别用二进制读取器读取多边形的边界盒、段数、点数、第一个点的位置、点集。

图5 PolygonShp属性及对应字节长度图
在这里插入图片描述
图6 readPoint()代码图
在这里插入图片描述
图7 readPolygon()代码图
在这里插入图片描述

1.4. GetFieldValue()

此函数用于返回标注,先将myds的第一张表,即写入shp文件属性表的表转化为DataTable格式,再返回其指定行指定列的字段值。

图8 GetFieldValue()代码图
在这里插入图片描述

1.5.GetHeadInformation()

已填充的属性变量有文件代码,于是继续填充文件长度、文件版本、shp类型、边界盒等变量。index表示相对于文件头开始位置的长度,i与每个域的长度有关。
图9 GetHeadInformation()代码图
在这里插入图片描述

1.6.获取文件代码和文件长度

文件代码和文件长度是大字节序列存放,在计算机中是反序存储的,故先使用Array.Reverse()将字节数组倒序,再转为32位整型即可得到正确结果;文件版本为小字节序列,直接转为32位整型。

图10 GetFileCode()和GetFileLength()代码图
在这里插入图片描述

2.Form1.cs

Form1.cs主要完成Shp.cs中方法的调用,实现地图、地图概况、属性表的展示,以及地图平移、缩放等功能,思维导图如下:

图11 Form1.cs思维导图
在这里插入图片描述

2.1.打开文件预处理

打开文件之前,如果多边形列表不为空,需先清空,以防出现多个图层叠加;并预定义一个location作为缩放坐标的基准点。

图12 打开文件预处理代码图
在这里插入图片描述

2.2.保存至多边形列表

将shpContent转化为PolygonShp格式,将其坐标点坐标转换为屏幕坐标,并添加到多边形列表listPolygonshp中。

图13 保存至多边形列表代码图
在这里插入图片描述

2.3.DisplayShp()

绘图函数中主要包括多边形边界、标注的绘制,颜色的填充。标注绘制需指定标注的坐标,即多边形x,y坐标的平均值;颜色是以“面积/周长”分段绘制分级色彩。这里的“面积/周长”为紧凑度计算公式 的简化,颜色越深表示紧凑度越高。

图14 DisplayShp()的多边形边界、颜色部分
在这里插入图片描述
图15 DisplayShp()的标注部分
在这里插入图片描述

2.4.GetShpMath()

DisplayShp()中计算“周长/面积”,需在Shp.cs中定义一个获取多边形周长或面积的函数,返回属性表数据集中的指定字段值。

图16 GetShpMath()代码图
在这里插入图片描述

2.5.填充地图概况文本框

打开shp文件后,用预定义的ShpHead对象获取头文件信息,并分段调用返回对应头文件信息的函数,GetFileCode()、GetFileLength()等输出在textBox1中。

图17 填充地图概况文本框代码图
在这里插入图片描述

2.6.FillProptyTable()

显示属性表,通过Shp的GetDataSet方法将属性表数据集赋给datagridview1的数据源。

图18 FillProptyTable()代码图
在这里插入图片描述

2.7.transform()

在填充至多边形列表前,需进行坐标转换,使原西安80坐标系的坐标点能在屏幕上同比例显示。故先定义两个变量储存x,y方向的最大、最小坐标之差,并令二者之比为比例;再令x坐标转换为原坐标与最小坐标之差占最大差的比值乘以比例加上固定初值,y坐标转换为固定值减去原坐标与最小坐标之差占最大差的比值。

图19 transform()代码图
在这里插入图片描述

2.8.地图缩放

在pictureBox1的鼠标滚轮事件中,当e.Delta > 0时即鼠标滚轮向上滚动,则缩放因子>1,反之<1,进而调用zoom()函数。这个函数的作用是按照缩放因子,对多边形坐标点进行缩放操作。

图20 地图缩放代码图
在这里插入图片描述

图21 zoom()代码图
在这里插入图片描述

2.9.地图平移

定义一个bool变量Moving,为true时表示正在平移。鼠标按下时记录初始点的坐标,松开时Moving置为false。移动过程中,所有多边形的坐标点相应变换对应数值,并同步改变初始点的坐标值防止重复计算。

图22 地图平移代码图
在这里插入图片描述

2.10.选择多边形

首先点击“选择”,将mouseStyle赋为1,即可开始选择;取消选择将会将控制选择的变量置为初始值,并取消多边形和属性表的被选状态。

图23 选择与取消选择代码图
在这里插入图片描述

实现选择多边形关键是判断鼠标点击位置是否在多边形内,故先用射线算法判断:遍历多边形的点集,判断鼠标点击点与p1,p2连成的射线的斜率。如果鼠标点击点在p2下方、p1上方,当时,点在多边形内;如果鼠标点击点在p1下方,p2上方,则相反时点在多边形内。

图24 判断点是否在多边形内算法图
在这里插入图片描述

在pictureBox1的鼠标点击事件中,首先定义一个点获取鼠标左键点击的坐标,再遍历多边形,判断鼠标点击在哪一个范围内,当鼠标点击点在某个多边形内,则IsInPolygon(clickPoint, ls.Points)返回true,获取到多边形的记录号,即可以实现绘制高亮、同步选择属性表记录等。

图25 点击选择事件代码图
在这里插入图片描述

在DataGridView1的RowHeaderMouseClick事件中,当点击了属性表某一行,则会选择到对应的多边形,实现了多边形与属性记录的双向选择功能。

图26 RowHeaderMouseClick事件代码图
在这里插入图片描述

在DisplayShp()中,生命一个PolygonShp,表示被选中的多边形,并用红色高亮进行填充。

图27 声明被选中多边形代码图
在这里插入图片描述

图28 高亮填充多边形代码图
在这里插入图片描述

二、运行结果

点击“文件”-“打开”,即可呈现地图、地图概况和属性表;点击退出将退出程序。

1、地图展示

图29 地图展示图
在这里插入图片描述

2、地图概况

设置textBox1为只读,地图概况展示如下:

图30 地图概况展示图
在这里插入图片描述

3、属性表

设置dataGridView1属性为只读,且不可增加行,属性表展示如下:

图31 属性表展示图
在这里插入图片描述

4、地图缩放

图32 地图缩小图
在这里插入图片描述

图33 地图放大图
在这里插入图片描述

5、地图平移

图34 地图向左上平移图
在这里插入图片描述

图35 地图向右下图
在这里插入图片描述

6、选择多边形

点击“选择”,在地图中点击多边形,若点落在多边形内,则该多边形将被红色高亮填充,对应属性表记录也会被选择;反正,先选择属性表记录也会使得对应多边形被选择。点击“取消选择”,地图和属性表将取消被选择的多边形和记录。

图36 选择多边形图

在这里插入图片描述

图37 属性表记录被选中图
在这里插入图片描述

图38 选择属性表记录图
在这里插入图片描述

图39 多边形被选中图

在这里插入图片描述

图40 取消选择图
在这里插入图片描述

三、源码:

1、shp.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
namespace ShpRead
{
    public class Shp
    {
        /* 下面数据成员是Shp文件头信息,具体参考设计指导书*/
        public ShpHead shpHead = new ShpHead();
        // ----- 以上是Shp文件文件头类-----//

        FileStream fs = null;
        public static BinaryReader bRead = null;
        OleDbDataAdapter myadapter;
        DataSet myds;
        string[] fields;

        public byte[] mainHead;

        public DataSet GetDataSet()
        {
            return myds;
        }

        //获取多边形面积或周长
        public double GetShpMath(int objectID, int index)
        {
            if (myds == null) return 0;
            else
            {  //直接返回数据集中的字段值。
                DataTable dt = (DataTable)myds.Tables[0];
                return double.Parse(dt.Rows[objectID][index].ToString());
            }
        }

        public string GetFieldValue(int objectID, int index)//返回一个字符型变量。
        {
            if (myds == null) return "";
            else
            {  //直接返回数据集中的字段值。
                DataTable dt = (DataTable)myds.Tables[0];
                return dt.Rows[objectID][index].ToString();
            }
        }
        public int OpenShpFile(string filename)
        {
            try{
            fs = new FileStream(filename,FileMode.Open,FileAccess.Read);
            }catch(Exception e)
            {
                MessageBox.Show("打开文件失败: "+e.Message);
                return -1;

            }

            //实例化二进制读取器
            bRead = new BinaryReader(fs);
            mainHead = bRead.ReadBytes(100);

            //获取文件名属性
            string filestr = filename.Remove(filename.LastIndexOf("."));
            string dbfname = filestr.Substring(filestr.LastIndexOf('\\') + 1);//文件名
            string dbfdire = filestr.Remove(filestr.LastIndexOf('\\'));//目录名
            Console.WriteLine(dbfdire);

            //连接dbf文件
            string str, sqlstr;
            OleDbConnection conn = new OleDbConnection();
            str = "provider=microsoft.jet.oledb.4.0; data source=" + dbfdire + ";Extended Properties=dBASE IV;";
            conn.ConnectionString = str;
            conn.Open();
            OleDbCommand comm = new OleDbCommand();
            sqlstr = "select * from " + dbfname;
            comm.Connection = conn;
            comm.CommandText = sqlstr;

            //实例化数据适配器对象
            myadapter = new OleDbDataAdapter(comm);
            myds = new DataSet();
            myadapter.Fill(myds, dbfname);

            return 1;//正确返回1
        }

        //读取点shape记录
        public PointShp readPoint()
        {
            PointShp ps = new PointShp();
            ps.pf.X = (float)bRead.ReadDouble();
            ps.pf.Y = (float)bRead.ReadDouble();
            return ps;

        }

        //读取折线或多边形shape记录
        public PolygonShp readPolygon()
        {
            PolygonShp pgs = new PolygonShp();
            //边界盒的读取
            for (int i = 0; i < 4; i++)
            {
                pgs.Box[i] = bRead.ReadDouble();
            }

            //段数和点数的读取
            pgs.NumParts = bRead.ReadInt32();
            pgs.NumPoints = bRead.ReadInt32();

            pgs.FirstPointIndexInParts = new Int32[pgs.NumParts];
            pgs.Points = new PointF[pgs.NumPoints];

            //读取第一个点的位置
            for (int i = 0; i < pgs.NumParts; i++)
            {
                pgs.FirstPointIndexInParts[i] = bRead.ReadInt32();
            }

            //读取组成多边形或折现点点集
            for (int i = 0; i < pgs.NumPoints; i++)
            {
                PointF p = new PointF();
                p.X = (float)bRead.ReadDouble();
                p.Y = (float)bRead.ReadDouble();
                pgs.Points[i] = p;
            }
            return pgs;
        }

        public int GetNextShp(out int shptype, out object shpContent, out int recordID)//获取下一个shp
        {
            byte[] recordhead = new byte[4];//声明并创建数据实例
            shptype = 0; recordID = 0; shpContent = null;//必须要先初始化赋值
            #region 读取点shp, shptype = 1
            if (shpHead.GetFileType() == 1)//Point,此处仅仅作为示例,对于点对象不需要补充代码
            {
                if (bRead.Read(recordhead, 0, 4) < 4)
                {
                    shptype = 0; recordID = 0; shpContent = null;
                    return 0;
                }
                byte[] rid = new byte[4];
                rid[0] = recordhead[3];
                rid[1] = recordhead[2];
                rid[2] = recordhead[1];
                rid[3] = recordhead[0];
                recordID = BitConverter.ToInt32(rid, 0);
                byte[] contentLength = new byte[4];
                if (bRead.Read(contentLength, 0, 4) < 4)
                {
                    shptype = 0; recordID = 0; shpContent = null;
                    return 0;
                };
                rid[0] = contentLength[3];
                rid[1] = contentLength[2];
                rid[2] = contentLength[1];
                rid[3] = contentLength[0];
                int count = BitConverter.ToInt32(rid, 0);
                count = count * 2;
                byte[] content = new byte[count];
                if (bRead.Read(content, 0, count) < count)
                {
                    shptype = 0; recordID = 0; shpContent = null;
                    return 0;
                }

                shptype = BitConverter.ToInt32(content, 0);
                shpContent = new PointShp();
                PointShp tmp = (PointShp)shpContent;
                tmp.pf = new PointF();
                tmp.pf.X = (float)BitConverter.ToDouble(content, 4);
                tmp.pf.Y = (float)BitConverter.ToDouble(content, 12);
                return 1;
            }
            #endregion 点处理完成
            # region 读取线代码  shptype = 3
            else if (shpHead.GetFileType() == 3)//线对象读取不需要实现
            {
                shptype = -1; shpContent = null; recordID = -1;
                return 1;
            }
            #endregion 线读取完成
            #region 读取多边形,代码跟线一样 shptype = 5
            else if (shpHead.GetFileType() == 5)
            {
                if (bRead.PeekChar() == -1) return 0;//没有可用字符或者流不支持查找时为 -1。
                recordhead = bRead.ReadBytes(4);//获取4个字符,记录头
                Array.Reverse(recordhead);
                recordID = BitConverter.ToInt32(recordhead, 0);//从0位置开始转换4个字节,记录头的记录数目号
                bRead.ReadBytes(4);
                byte[] shapetype = bRead.ReadBytes(4);//shp类型
                shptype = BitConverter.ToInt32(shapetype, 0);//记录内容的shp类型
                switch (shptype)
                {
                    case 1:
                        PointShp ps = readPoint(); ps.RecordID = recordID;
                        ps.label = GetFieldValue(ps.RecordID-1, 2); shpContent = ps; break;
                    case 2:
                    case 5:
                        PolygonShp pgs = readPolygon(); pgs.RecordID = recordID;
                        pgs.label = GetFieldValue(pgs.RecordID-1, 2); shpContent = pgs; break;
                    default: shpContent = null; break;
                }
                return 1;
            }
            #endregion
            #region 其他的不理
            else
            {
                shptype = 0; recordID = 0; shpContent = null;
                return 0;
            }
            #endregion
        }
        public void CloseShpFile()
        {
            bRead.Close();
            fs.Close();
        }

    }
    public class PointShp
    {
        public Int32 RecordID;
        public PointF pf;
        public string label;
    }
    public class LinesShp
    {
        public Int32 RecordID;
        public double[] Box = new double[4];
        public Int32 NumParts;
        public Int32 NumPoints;
        public Int32[] FirstPointIndexInParts;
        public PointF[] Points;
        public string label;

    }
    public class PolygonShp
    {
        public Int32 RecordID;
        public double[] Box = new double[4];
        public Int32 NumParts;
        public Int32 NumPoints;
        public Int32[] FirstPointIndexInParts;
        public PointF[] Points;
        public string label;
    }


    public class ShpHead
    {
        public ShpHead()
        {
        }
        /* 下面数据成员是Shp文件头对应的分段信息,具体参考设计指导书*/
        byte[] filecode = { 0, 0, 0, 0 };
        byte[] rev1 = { 0, 0, 0, 0 };
        byte[] rev2 = { 0, 0, 0, 0 };
        byte[] rev3 = { 0, 0, 0, 0 };
        byte[] rev4 = { 0, 0, 0, 0 };
        byte[] rev5 = { 0, 0, 0, 0 };
        byte[] filelength = { 0, 0, 0, 0 };
        byte[] filever = { 0, 0, 0, 0 };
        byte[] shpType = { 0, 0, 0, 0 };
        byte[] xmin = { 0, 0, 0, 0, 0, 0, 0, 0 };
        byte[] ymin = { 0, 0, 0, 0, 0, 0, 0, 0 };
        byte[] xmax = { 0, 0, 0, 0, 0, 0, 0, 0 };
        byte[] ymax = { 0, 0, 0, 0, 0, 0, 0, 0 };
        byte[] zmin = { 0, 0, 0, 0, 0, 0, 0, 0 };
        byte[] zmax = { 0, 0, 0, 0, 0, 0, 0, 0 };
        byte[] mmin = { 0, 0, 0, 0, 0, 0, 0, 0 };
        byte[] mmax = { 0, 0, 0, 0, 0, 0, 0, 0 };
        // ----- 以上是Shp文件文件头信息-----// //
        /*
                * GetHeadInformation填充Shp的属性成员,将文件头的100个字节分别填到相应的属性变量中
                * 
                */
        public int GetHeadInformation(byte[] mainHead)
        {

            if (mainHead.Length != 100)
            {
                return -1;
            }
            int index = 0;
            for (int i = 0; i < 4; i++, index++)
            {
                filecode[i] = mainHead[index];
            }
            for (int i = 0; i < 4; i++, index++)
            {
                rev1[i] = mainHead[index];
            }
            for (int i = 0; i < 4; i++, index++)
            {
                rev2[i] = mainHead[index];
            }
            for (int i = 0; i < 4; i++, index++)
            {
                rev3[i] = mainHead[index];
            }
            for (int i = 0; i < 4; i++, index++)
            {
                rev4[i] = mainHead[index];
            }
            for (int i = 0; i < 4; i++, index++)
            {
                rev5[i] = mainHead[index];
            }
            //----以下你需要补充完整代码--------// 
            for (int i = 0; i < 4; i++, index++)
            {
                filelength[i] = mainHead[index];
            }
            for (int i = 0; i < 4; i++, index++)
            {
                filever[i] = mainHead[index];
            }
            for (int i = 0; i < 4; i++, index++)
            {
                shpType[i] = mainHead[index];
            }
            for (int i = 0; i < 8; i++, index++)
            {
                xmin[i] = mainHead[index];
            }
            for (int i = 0; i < 8; i++, index++)
            {
                ymin[i] = mainHead[index];
            }
            for (int i = 0; i < 8; i++, index++)
            {
                xmax[i] = mainHead[index];
            }
            for (int i = 0; i < 8; i++, index++)
            {
                ymax[i] = mainHead[index];
            }
            for (int i = 0; i < 8; i++, index++)
            {
                zmin[i] = mainHead[index];
            }
            for (int i = 0; i < 8; i++, index++)
            {
                zmax[i] = mainHead[index];
            }
            for (int i = 0; i < 8; i++, index++)
            {
                mmin[i] = mainHead[index];
            }
            for (int i = 0; i < 8; i++, index++)
            {
                mmax[i] = mainHead[index];
            }

            return 1;
        }

        public int GetFileCode()
        {
            int kl = 0;
            //  此处补充代码,返回文件代码,若不是9994,请检查你的代码  ---// 
            Array.Reverse(filecode);
            kl = BitConverter.ToInt32(filecode, 0);
            return kl;
        }
        public int GetFileLength()
        {
            int kl = 0;
            //补充代码----  返回文件长度---
            Array.Reverse(filelength);
            kl = BitConverter.ToInt32(filelength, 0);
            return kl * 2;// 为什么乘以2  ????指导书中第三页倒数第八行有说明

        }
        public int GetFileVer()
        {
            int kl = 0;
            /*
              * 功能: 返回文件版本 对应filever
              * 补充代码
              */
            kl = BitConverter.ToInt32(filever, 0);
            return kl;
        }
        public int GetFileType()
        {
            int kl = 0;
            //补充代码---  返回类型 对应shpType变量/// 
            kl = BitConverter.ToInt32(shpType, 0);
            return kl;
        }
        public double GetXMin()
        {
            //获得边界盒中的x最小值---
            double kl = 0;
            kl = BitConverter.ToDouble(xmin, 0);
            return kl;

        }
        //------以下函数完整,不需修改---
        public double GetXMax() //获得边界盒中的x最大值--
        {
            double kl = 0;
            kl = BitConverter.ToDouble(xmax, 0);
            return kl;
        }

        public double GetYMin()// 获得边界盒中的Y最小值--,以下函数类似//
        {
            return BitConverter.ToDouble(ymin, 0);
        }
        public double GetYMax()
        {
            return BitConverter.ToDouble(ymax, 0);
        }
        public double GetMMin()
        {
            return BitConverter.ToDouble(mmin, 0);
        }
        public double GetMMax()
        {
            return BitConverter.ToDouble(mmax, 0);
        }
        public double GetZMin()
        {
            return BitConverter.ToDouble(zmin, 0);
        }
        public double GetZMax()
        {
            return BitConverter.ToDouble(zmax, 0);
        }
    }
}

2、Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ShpRead;
using System.IO;
using System.Data.OleDb;
namespace ShpRead
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        List<PointShp> listPointShp = new List<PointShp>();
        List<LinesShp> listLineShp = new List<LinesShp>();
        List<PolygonShp> listPolygonshp = new List<PolygonShp>();
        OleDbDataAdapter myadapter;
        DataSet myds;
        ShpRead.Shp kl = new Shp();

        float score = 450;
        float LargeOrSmallRatio = 1.0f;

        //地图平移变量
        float xStart;
        float yStart;
        bool Moving;

        PointF location;

        int selectindex = -1;
        int mouseStyle = 0;

        private void 打开ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (listPolygonshp != null) listPolygonshp.Clear();

            location = new PointF((this.Width - score) / 2, 25);//为移动以及缩放时地图的左上角进行定位,方便其他的操作

            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "shp文件|*.shp";
            if (ofd.ShowDialog() == DialogResult.Cancel) return;
            kl.OpenShpFile(ofd.FileName);

            //填充数据集
            myds = kl.GetDataSet();

            //填充地图概况文本框的内容
            kl.shpHead.GetHeadInformation(kl.mainHead);
            textBox1.Text = "";
            textBox1.Text += "文件代码:" + kl.shpHead.GetFileCode() + "\r\n"
             + "文件长度:" + kl.shpHead.GetFileLength() + "\r\n"
             + "文件版本:" + kl.shpHead.GetFileVer() + "\r\n"
             + "shp类型:" + kl.shpHead.GetFileType() + "\r\n"
             + "X最小值:" + kl.shpHead.GetXMin() + "\r\n"
             + "X最大值:" + kl.shpHead.GetXMax() + "\r\n"
             + "y最小值:" + kl.shpHead.GetYMin() + "\r\n"
             + "y最大值:" + kl.shpHead.GetYMax() + "\r\n"
             + "z最小值:" + kl.shpHead.GetZMin() + "\r\n"
             + "z最大值:" + kl.shpHead.GetZMax() + "\r\n"
             + "m最小值:" + kl.shpHead.GetMMin() + "\r\n"
             + "m最大值:" + kl.shpHead.GetMMax();

            //记录内容的读取
            object shpContent;
            int shptype;
            int recordid;
            while (kl.GetNextShp(out shptype, out shpContent, out recordid) != 0)
            {
                if (shptype == 5)
                {
                    PolygonShp pgs = (PolygonShp)shpContent;
                    transform(pgs.Points, (int)score);
                    listPolygonshp.Add(pgs);
                }
            }
            kl.CloseShpFile();
            FillProptyTable();//填充属性表
            DisplayShp();//绘图
        }

        private void transform(PointF[] points, int lenth)
        {
            //坐标转换
            //shape文件读取的图形具体大小:宽,高,以及宽高比
            float width = (float)(kl.shpHead.GetXMax() - kl.shpHead.GetXMin());
            float height = (float)(kl.shpHead.GetYMax() - kl.shpHead.GetYMin());
            float biLi = width / height;

            //将绘制的各点的坐标进行相应的转换,方便在窗体上的显示
            for (int i = 0; i < points.Length; i++)
            {
                points[i].X = (this.Width - lenth) / 2 + (points[i].X - (float)kl.shpHead.GetXMin()) / width * lenth * biLi;
                points[i].Y = lenth - (points[i].Y - (float)kl.shpHead.GetYMin()) / height * lenth;
            }
        }

        public void DisplayShp()
        {
            Bitmap bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            Graphics g = Graphics.FromImage(bitmap);

            PolygonShp currentls = null;
            if (selectindex != -1)
                currentls = listPolygonshp[selectindex];

            foreach (PolygonShp ls in listPolygonshp)
            {
                Pen pen = new Pen(Color.Black, 2);
                g.DrawPolygon(pen, ls.Points);//绘制地图

                //计算面积与周长的比值
                double cr = kl.GetShpMath(ls.RecordID - 1, 11) / kl.GetShpMath(ls.RecordID - 1, 10); 

                Color c = Color.Red;
                if (cr > 500 && cr < 2000)
                    c = Color.FromArgb(255, 255, 192);
                if (cr > 2000 && cr < 3000)
                    c = Color.FromArgb(255, 192, 128);
                if (cr > 3000 && cr < 4000)
                    c = Color.FromArgb(255, 192, 128);
                if (cr > 4000)
                    c = Color.FromArgb(255, 128, 0);

                Brush bu = new SolidBrush(c);
                g.FillPolygon(bu, ls.Points);
                
                //多边形高亮
                if (ls == currentls)
                    g.FillPolygon(new SolidBrush(Color.Red), currentls.Points);

                float w = 0;
                float h = 0;
                //计算坐标总和
                foreach (PointF pf in ls.Points)
                {
                    w += pf.X;
                    h += pf.Y;
                }
                //计算每个标注位置
                PointF point = new PointF();
                point.X = w / ls.Points.Length - 25;
                point.Y = h / ls.Points.Length;

                g.DrawPolygon(pen, ls.Points);
                g.DrawString(ls.label, new Font("宋体",9), Brushes.Blue, point);//绘制标注字段

                pen.Dispose();
            }

            if (pictureBox1.Image != null)
            { pictureBox1.Image.Dispose(); pictureBox1.Image = null; }
            pictureBox1.Image = bitmap;
            g.Dispose();

        }
        public void FillProptyTable()
        {
            //填充datagridview,用来填充的数据集来自Shp的成员函数GetDataSet
            dataGridView1.DataSource = myds.Tables[0];
        }

        private void 属性ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            textBox1.Visible = !textBox1.Visible;
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            DisplayShp();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void zoom(PointF[] points, float zoomnum)
        {
            //图形缩放,根据传入的zoomnum参数调整相关点集组成图形的大小
            //缩放基于图形的左上角
            for (int i = 0; i < points.Length; i++)
            {
                points[i].X = location.X + (points[i].X - location.X) * zoomnum;
                points[i].Y = location.Y + (points[i].Y - location.Y) * zoomnum;
            }
        }

        private void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
        {
            //首先在Designer.cs文件的pictureBox1部分添加以下事件声明,以创建鼠标滚动事件。
            //this.pictureBox1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseWheel);
            //通过滚轮进行缩放
            Console.WriteLine(e.Delta);
            float zoomnum;//缩放因子
            if (e.Delta > 0)
            {
                zoomnum = 1.1f;
            }
            else
            {
                zoomnum = 0.9f;
            }
            foreach (PolygonShp ls in listPolygonshp)
            {
                zoom(ls.Points, zoomnum);
            }
            DisplayShp();
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (Moving)
            {
                foreach (PolygonShp ls in listPolygonshp)
                {
                    for (int i = 0; i < ls.Points.Length; i++)
                    { 
                        ls.Points[i].X += e.X - xStart;
                        ls.Points[i].Y += e.Y - yStart;
                    }
                }
                xStart = e.X;
                yStart = e.Y;
                DisplayShp();
            }
        }
        
        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;
            Moving = true;
            xStart = e.X;
            yStart = e.Y;
        }

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            Moving = false;
        }

        private void 退出ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        public bool IsInPolygon(PointF checkPoint, PointF[] polygonPoints)
        {
            //判断是否在多边形内
            bool inside = false;
            int pointCount = polygonPoints.Length;
            PointF p1, p2;

            for (int i = 0, j = pointCount - 1; i < pointCount; j = i, i++)
            {
                p1 = polygonPoints[i];
                p2 = polygonPoints[j];
                if (checkPoint.Y < p2.Y)
                {//p2在射线之上
                    if (p1.Y <= checkPoint.Y)
                    {//p1正好在射线中或者射线下方

                        if ((checkPoint.Y - p1.Y) * (p2.X - p1.X) > (checkPoint.X - p1.X) * (p2.Y - p1.Y))
                        {
                            inside = (!inside);
                        }
                    }
                }
                else if (checkPoint.Y < p1.Y)
                {
                    //p2正好在射线中或者在射线下方,p1在射线上
                    if ((checkPoint.Y - p1.Y) * (p2.X - p1.X) < (checkPoint.X - p1.X) * (p2.Y - p1.Y))
                    {
                        inside = (!inside);
                    }
                }
            }
            return inside;
        }

        private void dataGridView1_RowHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            mouseStyle = 1;
            selectindex = e.RowIndex;
            DisplayShp();
        }

        private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
        {
            Point clickPoint = new Point(e.X, e.Y);
            if (e.Button == MouseButtons.Left && mouseStyle == 1)
            {//遍历多边形,判断鼠标在哪一个范围内
                foreach (PolygonShp ls in listPolygonshp)
                {
                    if (IsInPolygon(clickPoint, ls.Points))
                    {
                        selectindex = ls.RecordID - 1;
                        dataGridView1.CurrentCell = dataGridView1.Rows[selectindex].Cells[0];
                        DisplayShp();
                        break;
                    }
                }
            }
        }

        private void 选择ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            mouseStyle = 1;
        }

        private void 取消选择ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if(selectindex!=-1)
                dataGridView1.Rows[selectindex].Selected = false;
            selectindex = -1;
            mouseStyle = 0;
            DisplayShp();
        }
    }
}

四、源文件压缩包下载

https://download.csdn.net/download/qq_48607161/20398599

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值