⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 shpreader.cs

📁 实现SHP
💻 CS
📖 第 1 页 / 共 2 页
字号:
///GeoCon, free tool to create gml & svg from gis files. 
///Copyright(C) 2005 Amri Rosyada
///Distributed under GNU-LGPL, see a copy of the license in root directory

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

using GeoCon;
using GeoCon.Data;
using GeoCon.dbf;
using gml;

namespace GeoCon.shp
{
	/// <summary>
	/// A class for reading arcview shapefile(*.shp), including its index(*.shx) and data(*.dbf).
	/// </summary>
	public class ShapeReader
	{
		#region private fields
		private short shpFileCode = 9994;
		private int RecordCount;
		private int currentShapeType;

		private BinaryReader shpReader;
		private BinaryReader shxReader;
		private dbf.DbfReader dbfReader;
		private string name;
		//private const string defaultsrs = "http://www.opengis.net/gml/srs/epsg.xml#4326";
		private string defaultsrs = " ";

		//private dbf.Header dbHeader;
		private shp.RecordHeader[] recordIndex;
		#endregion

		#region public fields

		/// <summary>
		/// Occurs when there's noticable change in this reader progress.
		/// </summary>
		public event GeoCon.Data.StatusEventHandler StatusChange;
		private GeoCon.Data.StatusEventArgs StatusArgs = new GeoCon.Data.StatusEventArgs("",0); 

		/// <summary>Holds various info about the shapefile</summary>
		public shp.ShapeHeader mainHeader;
		//public gml.AbstractGeometryType[] Shapes;

		/// <summary>Field collection of dbf and geometry data.</summary>
		public Data.FieldCollection Fields;
		#endregion

		#region constructor
		/// <summary>
		/// Constructs new ShapeReader
		/// </summary>
		/// <param name="shapepath">the full path to a shapefile(*.shp)</param>
		public ShapeReader(string shapepath)
		{
			try
			{
				string fne =System.IO.Path.GetFileNameWithoutExtension(shapepath);
				name=fne;
				string shxpath = fne + ".shx";
				string dbfpath = fne + ".dbf";

				shpReader = new BinaryReader(File.Open(shapepath,FileMode.Open,FileAccess.Read,FileShare.Read));
				shxReader = new BinaryReader(File.Open(shxpath,FileMode.Open,FileAccess.Read,FileShare.Read));
				if(File.Exists(dbfpath)) dbfReader = new dbf.DbfReader(dbfpath);
				shxpath=null;
				dbfpath=null;
			}
			catch(FileNotFoundException fnfe)
			{
				//System.Windows.Forms.MessageBox.Show("file '"+fnfe.FileName+"' not found.");
				throw new FileNotFoundException(fnfe.Message); 
			}
			catch(System.Exception ioe)
			{
				//System.Windows.Forms.MessageBox.Show("error : \r"+ioe.Message);
				throw new Exception(ioe.Message,ioe.InnerException);   
			}

		}
		#endregion

		#region public methods

		/// <summary>
		/// Begin read .shp, .shx, and .dbf file.
		/// </summary>
		/// <returns>The MapData which holds shapes & data.</returns>
		public MapData Read()
		{
			UpdateStatus("reading "+name+"..",0);
			MapData mapdata = new MapData();
			mapdata.Name=name;
			mainHeader = new shp.ShapeHeader();
			if(dbfReader!=null) dbfReader.Read(ref mapdata);
			readShapeHeader(ref mapdata);
			readIndexHeader();
			readRecordHeader(ref mapdata);
			mapdata.Info.FileDirectory = System.Environment.CurrentDirectory;//System.IO.Path.GetDirectoryName(name);
			mapdata.Info.FileGeometry  = name+".shp";
			mapdata.Info.FileData =(dbfReader!=null)? name+".dbf" : "not found";
			mapdata.Info.FileIndex = name+".shx";
			mapdata.Info.RecordsCount = RecordCount;

			UpdateStatus("Finished reading "+name,0);

			return mapdata;
		}
		/// <summary>
		/// close readers/streams used in this reader.
		/// </summary>
		public void Close()
		{	
			if(dbfReader!=null) dbfReader.Close();
			shpReader.Close();
			shxReader.Close();
		}
		#endregion

		#region read header
		/// <summary>
		/// 
		/// </summary>
		/// <param name="mapdata"></param>
		private void readShapeHeader(ref MapData mapdata)
		{
			shp.ShapeFileHeader fh = mainHeader.shape;
			mainHeader.shape.FileCode = SwapEndian32(shpReader.ReadInt32());
			if (mainHeader.shape.FileCode != shpFileCode)
			{
				shpReader.Close();
				throw new Exception("filetype unknown, "+mainHeader.shape.FileCode.ToString());
			}
			//skip 5 * int32, me know not what are they
			shpReader.BaseStream.Seek(20,SeekOrigin.Current);
			mainHeader.shape.FileLength = SwapEndian32(shpReader.ReadInt32());
			mainHeader.shape.version = shpReader.ReadInt32();
			mainHeader.shape.shapeType = shpReader.ReadInt32();

			mapdata.Info.FileVersion = mainHeader.shape.version.ToString();
			mapdata.Info.BoundsLeft = shpReader.ReadDouble();
			mapdata.Info.BoundsBottom = shpReader.ReadDouble();
			mapdata.Info.BoundsRight = shpReader.ReadDouble();
			mapdata.Info.BoundsTop = shpReader.ReadDouble();

			mapdata.BoundingBox  = new gml.BoxType(new double[4]{mapdata.Info.BoundsLeft,mapdata.Info.BoundsTop,mapdata.Info.BoundsRight,mapdata.Info.BoundsBottom});
		}

		/// <summary>
		/// 
		/// </summary>
		private void readIndexHeader()
		{
			//byte start offset = 1
			mainHeader.index.FileCode = SwapEndian32(shxReader.ReadInt32());

			//skip 5 of 4-bytes integer , me know not what them
			shxReader.BaseStream.Seek(20,SeekOrigin.Current);

			//byte start offset = 25
			mainHeader.index.FileLength = SwapEndian32(shxReader.ReadInt32());
			RecordCount = (mainHeader.index.FileLength - 50) / 4;
		}

		/// <summary>
		/// Reads header for each shape. 
		/// </summary>
		/// <param name="mapdata"></param>
		private void readRecordHeader(ref MapData mapdata)
		{
			recordIndex = new shp.RecordHeader[RecordCount];
			GeometryField fgeo = new Data.GeometryField();
			mapdata.Fields.Add(fgeo);
			
			shxReader.BaseStream.Seek(100,SeekOrigin.Begin);

			for (int i=0;i<recordIndex.Length;i++)
			{
				UpdateStatus("",100*i/RecordCount);

				recordIndex[i] = new shp.RecordHeader();
				recordIndex[i].indexOffset = SwapEndian32(shxReader.ReadInt32());
				recordIndex[i].indexLength = SwapEndian32(shxReader.ReadInt32());

				//TODO ?: check if shpoff bigger than the stream length ;
				long shpoff = recordIndex[i].indexOffset*2;	//we use long for large files;
				shpReader.BaseStream.Seek(shpoff,SeekOrigin.Begin);

				recordIndex[i].shapeNumber = SwapEndian32(shpReader.ReadInt32());
				recordIndex[i].shapeLength = SwapEndian32(shpReader.ReadInt32());

				//shape number start from 1
				if(recordIndex[i].shapeNumber!=(i+1)) throw new Exception("record number in shape file doesn't match index file : " + recordIndex[i].shapeNumber.ToString()+" vs "+(i+1).ToString());
				currentShapeType = shpReader.ReadInt32();
				if(currentShapeType != mainHeader.shape.shapeType) throw new Exception("shape type in record no "+i.ToString()+" doesn't match with the header");

				readSingleShape(i,ref fgeo);
			}
		}
		#endregion

		#region read shapes

		/// <summary>
		/// Read arcview shape
		/// </summary>
		/// <param name="index">record index, zero based</param>
		/// <param name="fgeo"></param>
		private void readSingleShape(int index, ref GeometryField fgeo)
		{
			if(index<recordIndex.GetLowerBound(0) || index > recordIndex.GetUpperBound(0) ) 
			{
				throw new ArgumentOutOfRangeException("index must be between "+recordIndex.GetLowerBound(0).ToString()+" and "+recordIndex.GetUpperBound(0).ToString()) ;
			}
			switch (currentShapeType)
			{
				case (int)shp.TYPE.typeNullShape :
					fgeo.Add(fgeo.NullSymbol);
					break;
				case (int)shp.TYPE.typePoint :
					readPoint(ref fgeo);
					break;
				case (int)shp.TYPE.typePointM :
					readPointM(ref fgeo);
					break;
				case (int)shp.TYPE.typePointZ :
					readPointM(ref fgeo);
					break;
				case (int)shp.TYPE.typeMultiPoint :
					readMultiPoint(ref fgeo);
					break;
				case (int)shp.TYPE.typeMultiPointM :
					readMultiPointM(ref fgeo);
					break;
				case (int)shp.TYPE.typeMultiPointZ :
					readMultiPointM(ref fgeo);
					break;
				case (int)shp.TYPE.typePolyLine :
					readPolyline(ref fgeo);
					break;
				case (int)shp.TYPE.typePolyLineM :
					readPolylineM(ref fgeo);
					break;
				case (int)shp.TYPE.typePolyLineZ :
					readPolylineM(ref fgeo);
					break;
				case (int)shp.TYPE.typePolygon :
					readPolygon(ref fgeo,false);
					break;
				case (int)shp.TYPE.typePolygonM :
					readPolygon(ref fgeo,true);
					break;
				case (int)shp.TYPE.typePolygonZ :
					readPolygon(ref fgeo,true);
					break;
				case (int)shp.TYPE.typeMultiPatch :
					readMultiPatch(ref fgeo,true);
					break;
				default:
					break;
			}
		}
		#endregion

		#region read point, pointM & Z
		/// <summary>
		/// Read a point
		/// </summary>
		/// <param name="fgeo"></param>
		private void readPoint(ref GeometryField fgeo)
		{
			double _x = shpReader.ReadDouble();
			double _y = shpReader.ReadDouble();
			gml.CoordinatesType _ct = new gml.CoordinatesType(new double[2]{_x,_y});
			gml.PointType pt = new gml.PointType(_ct);
			fgeo.Add(new gml.PointPropertyType(pt));
		}
		/// <summary>
		/// Reads PointM or PointZ, M values treated as Z values.
		/// </summary>
		/// <param name="fgeo"></param>
		private void readPointM(ref GeometryField fgeo)
		{
			double _x = shpReader.ReadDouble();
			double _y = shpReader.ReadDouble();
			double _z = shpReader.ReadDouble();
			gml.CoordinatesType _ct = new gml.CoordinatesType(new double[2]{_x,_y});
			_ct.hasZ = true;
			_ct.zArray=new double[1]{_z};
			gml.PointType pt = new gml.PointType(_ct);
			fgeo.Add(new gml.PointPropertyType(pt));
		}

		#endregion

		#region read multipoint, multipointM & Z
		/// <summary>
		/// Read multipoint
		/// </summary>
		/// <param name="fgeo"></param>
		private void readMultiPoint(ref GeometryField fgeo)
		{
			//multipoint bounding box
			double _left = shpReader.ReadDouble();
			double _bottom = shpReader.ReadDouble();
			double _right = shpReader.ReadDouble();
			double _top = shpReader.ReadDouble();

			int _partsCount = shpReader.ReadInt32();
			gml.PointType[] pts = new gml.PointType[_partsCount]; 
			double _x, _y;
			for (int j=0;j<_partsCount;j++)
			{
				_x = shpReader.ReadDouble();
				_y = shpReader.ReadDouble();
				gml.CoordinatesType _ct = new gml.CoordinatesType(new double[2]{_x,_y});
				pts[j] = new gml.PointType(_ct);
			}
			gml.MultiPointType mpt = new gml.MultiPointType(defaultsrs,pts);
			fgeo.Add(new gml.MultiPointPropertyType(mpt)); 
			pts=null;
			mpt=null;
		}
		/// <summary>
		/// Read MultiPointM or MultiPointZ, M values treated as Z values
		/// </summary>
		/// <param name="fgeo"></param>
		private void readMultiPointM(ref GeometryField fgeo)
		{
			//multipoint bounding box
			double _left = shpReader.ReadDouble();
			double _bottom = shpReader.ReadDouble();
			double _right = shpReader.ReadDouble();
			double _top = shpReader.ReadDouble();

			int _partsCount = shpReader.ReadInt32();
			gml.CoordinatesType[] cts=new CoordinatesType[_partsCount];
			double _x, _y;
			for (int j=0;j<_partsCount;j++)
			{
				_x = shpReader.ReadDouble();
				_y = shpReader.ReadDouble();
				cts[j]=new gml.CoordinatesType(new double[2]{_x,_y});
			}
			double miniValue = shpReader.ReadDouble();
			double maxiValue = shpReader.ReadDouble();
			gml.PointType[] pts = new gml.PointType[_partsCount]; 
			for (int j=0;j<_partsCount;j++) //each part contains one point
			{
				cts[j].hasZ=true;
				cts[j].zArray=new double[1]{shpReader.ReadDouble()};
				pts[j]=new PointType(cts[j]);
			}
			if(_partsCount>1)
			{
				gml.MultiPointType mpt = new gml.MultiPointType(defaultsrs,pts);
				fgeo.Add(new gml.MultiPointPropertyType(mpt)); 
				mpt=null;
			}
			else
			{
				fgeo.Add(new gml.PointPropertyType(pts[0])); 
			}
			cts=null;
			pts=null;
		}

		#endregion

		#region read polyline, polylineM & Z
		/// <summary>
		/// Reads a polyline
		/// </summary>
		/// <param name="fgeo"></param>
		private void readPolyline(ref GeometryField fgeo)
		{
			double _left = shpReader.ReadDouble();
			double _bottom = shpReader.ReadDouble();
			double _right = shpReader.ReadDouble();
			double _top = shpReader.ReadDouble();

			int _partsCount = shpReader.ReadInt32();
			int _totalPoints = shpReader.ReadInt32();
			int kend, dcount;
			gml.LineStringType[] LSArray = new gml.LineStringType[_partsCount]; 
			int[] _partPoints = readIntegerArray(shpReader,_partsCount);
			for (int j=0;j<_partPoints.Length;j++)
			{
				kend = (j!=(_partsCount-1))? _partPoints[j+1] : _totalPoints;
				dcount = (kend-_partPoints[j]);
				gml.CoordinatesType _ct = new gml.CoordinatesType(ReadTuples(shpReader,dcount));
				LSArray[j] = new gml.LineStringType(_ct); 
			}
			if(_partsCount>1) //multilinestring
			{
				gml.MultiLineStringType mls = new gml.MultiLineStringType(defaultsrs,LSArray);
				fgeo.Add(new MultiLineStringPropertyType(mls));
			}
			else //linestring
			{
				fgeo.Add(new gml.LineStringPropertyType(LSArray[0]));
			}
			LSArray=null;
			_partPoints=null;
		}
		/// <summary>
		/// Reads a polylineM or polylineZ, M values treated as Z values.
		/// </summary>
		/// <param name="fgeo">GeometryField to store the shape</param>
		private void readPolylineM(ref GeometryField fgeo)
		{
			double _left = shpReader.ReadDouble();
			double _bottom = shpReader.ReadDouble();
			double _right = shpReader.ReadDouble();
			double _top = shpReader.ReadDouble();

			int _partsCount = shpReader.ReadInt32();
			int _totalPoints = shpReader.ReadInt32();
			int kend, dcount;
			gml.CoordinatesType[] cts = new CoordinatesType[_partsCount];
			int[] _partPoints = readIntegerArray(shpReader,_partsCount);
			for (int j=0;j<_partPoints.Length;j++)
			{
				kend = (j!=(_partsCount-1))? _partPoints[j+1] : _totalPoints;
				dcount = (kend-_partPoints[j]);
				cts[j]=new gml.CoordinatesType(ReadTuples(shpReader,dcount));
			}
			double miniValue = shpReader.ReadDouble(); //or startlength?
			double maxiValue = shpReader.ReadDouble(); //or endlength?
			for (int j=0;j<_partPoints.Length;j++)
			{
				kend = (j!=(_partsCount-1))? _partPoints[j+1] : _totalPoints;
				dcount = (kend-_partPoints[j]);
				cts[j].hasZ=true;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -