📄 shapefile.cs
字号:
// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk)
//
// This file is part of SharpMap.
// SharpMap is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// SharpMap is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public License
// along with SharpMap; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Drawing;
namespace SharpMap.Data.Providers
{
/// <summary>
/// Shapefile geometry type.
/// </summary>
public enum ShapeType : int
{
/// <summary>
/// Null shape with no geometric data
/// </summary>
Null = 0,
/// <summary>
/// A point consists of a pair of double-precision coordinates.
/// SharpMap interpretes this as <see cref="SharpMap.Geometries.Point"/>
/// </summary>
Point = 1,
/// <summary>
/// PolyLine is an ordered set of vertices that consists of one or more parts. A part is a
/// connected sequence of two or more points. Parts may or may not be connected to one
/// another. Parts may or may not intersect one another.
/// SharpMap interpretes this as either <see cref="SharpMap.Geometries.LineString"/> or <see cref="SharpMap.Geometries.MultiLineString"/>
/// </summary>
PolyLine = 3,
/// <summary>
/// A polygon consists of one or more rings. A ring is a connected sequence of four or more
/// points that form a closed, non-self-intersecting loop. A polygon may contain multiple
/// outer rings. The order of vertices or orientation for a ring indicates which side of the ring
/// is the interior of the polygon. The neighborhood to the right of an observer walking along
/// the ring in vertex order is the neighborhood inside the polygon. Vertices of rings defining
/// holes in polygons are in a counterclockwise direction. Vertices for a single, ringed
/// polygon are, therefore, always in clockwise order. The rings of a polygon are referred to
/// as its parts.
/// SharpMap interpretes this as either <see cref="SharpMap.Geometries.Polygon"/> or <see cref="SharpMap.Geometries.MultiPolygon"/>
/// </summary>
Polygon = 5,
/// <summary>
/// A MultiPoint represents a set of points.
/// SharpMap interpretes this as <see cref="SharpMap.Geometries.MultiPoint"/>
/// </summary>
Multipoint = 8,
/// <summary>
/// A PointZ consists of a triplet of double-precision coordinates plus a measure.
/// SharpMap interpretes this as <see cref="SharpMap.Geometries.Point"/>
/// </summary>
PointZ = 11,
/// <summary>
/// A PolyLineZ consists of one or more parts. A part is a connected sequence of two or
/// more points. Parts may or may not be connected to one another. Parts may or may not
/// intersect one another.
/// SharpMap interpretes this as <see cref="SharpMap.Geometries.LineString"/> or <see cref="SharpMap.Geometries.MultiLineString"/>
/// </summary>
PolyLineZ = 13,
/// <summary>
/// A PolygonZ consists of a number of rings. A ring is a closed, non-self-intersecting loop.
/// A PolygonZ may contain multiple outer rings. The rings of a PolygonZ are referred to as
/// its parts.
/// SharpMap interpretes this as either <see cref="SharpMap.Geometries.Polygon"/> or <see cref="SharpMap.Geometries.MultiPolygon"/>
/// </summary>
PolygonZ = 15,
/// <summary>
/// A MultiPointZ represents a set of <see cref="PointZ"/>s.
/// SharpMap interpretes this as <see cref="SharpMap.Geometries.MultiPoint"/>
/// </summary>
MultiPointZ = 18,
/// <summary>
/// A PointM consists of a pair of double-precision coordinates in the order X, Y, plus a measure M.
/// SharpMap interpretes this as <see cref="SharpMap.Geometries.Point"/>
/// </summary>
PointM = 21,
/// <summary>
/// A shapefile PolyLineM consists of one or more parts. A part is a connected sequence of
/// two or more points. Parts may or may not be connected to one another. Parts may or may
/// not intersect one another.
/// SharpMap interpretes this as <see cref="SharpMap.Geometries.LineString"/> or <see cref="SharpMap.Geometries.MultiLineString"/>
/// </summary>
PolyLineM = 23,
/// <summary>
/// A PolygonM consists of a number of rings. A ring is a closed, non-self-intersecting loop.
/// SharpMap interpretes this as either <see cref="SharpMap.Geometries.Polygon"/> or <see cref="SharpMap.Geometries.MultiPolygon"/>
/// </summary>
PolygonM = 25,
/// <summary>
/// A MultiPointM represents a set of <see cref="PointM"/>s.
/// SharpMap interpretes this as <see cref="SharpMap.Geometries.MultiPoint"/>
/// </summary>
MultiPointM = 28,
/// <summary>
/// A MultiPatch consists of a number of surface patches. Each surface patch describes a
/// surface. The surface patches of a MultiPatch are referred to as its parts, and the type of
/// part controls how the order of vertices of an MultiPatch part is interpreted.
/// SharpMap doesn't support this feature type.
/// </summary>
MultiPatch = 31
};
/// <summary>
/// Shapefile dataprovider
/// </summary>
/// <remarks>
/// <para>The ShapeFile provider is used for accessing ESRI ShapeFiles. The ShapeFile should at least contain the
/// [filename].shp, [filename].idx, and if feature-data is to be used, also [filename].dbf file.</para>
/// <para>The first time the ShapeFile is accessed, SharpMap will automatically create a spatial index
/// of the shp-file, and save it as [filename].shp.sidx. If you change or update the contents of the .shp file,
/// delete the .sidx file to force SharpMap to rebuilt it. In web applications, the index will automatically
/// be cached to memory for faster access, so to reload the index, you will need to restart the web application
/// as well.</para>
/// <para>
/// M and Z values in a shapefile is ignored by SharpMap.
/// </para>
/// </remarks>
/// <example>
/// Adding a datasource to a layer:
/// <code lang="C#">
/// SharpMap.Layers.VectorLayer myLayer = new SharpMap.Layers.VectorLayer("My layer");
/// myLayer.DataSource = new SharpMap.Data.Providers.ShapeFile(@"C:\data\MyShapeData.shp");
/// </code>
/// </example>
public class ShapeFile : SharpMap.Data.Providers.IProvider, IDisposable
{
private ShapeType _ShapeType;
private string _Filename;
private SharpMap.Geometries.BoundingBox _Envelope;
private DbaseReader dbaseFile;
private FileStream fsShapeIndex;
private BinaryReader brShapeIndex;
private FileStream fsShapeFile;
private BinaryReader brShapeFile;
private bool _FileBasedIndex;
private bool _IsOpen;
private bool _CoordsysReadFromFile = false;
//private int[] _LengthOfRecord;
private int _FeatureCount;
/// <summary>
/// Tree used for fast query of data
/// </summary>
private SharpMap.Utilities.SpatialIndexing.QuadTree tree;
/// <summary>
/// Initializes a ShapeFile DataProvider without a file-based spatial index.
/// </summary>
/// <param name="filename">Path to shape file</param>
public ShapeFile(string filename) : this(filename,false) { }
/// <summary>
/// Initializes a ShapeFile DataProvider.
/// </summary>
/// <remarks>
/// <para>If FileBasedIndex is true, the spatial index will be read from a local copy. If it doesn't exist,
/// it will be generated and saved to [filename] + '.sidx'.</para>
/// <para>Using a file-based index is especially recommended for ASP.NET applications which will speed up
/// start-up time when the cache has been emptied.
/// </para>
/// </remarks>
/// <param name="filename">Path to shape file</param>
/// <param name="fileBasedIndex">Use file-based spatial index</param>
public ShapeFile(string filename, bool fileBasedIndex)
{
_Filename = filename;
_FileBasedIndex = fileBasedIndex;
//Initialize DBF
string dbffile = _Filename.Substring(0, _Filename.LastIndexOf(".")) + ".dbf";
if (File.Exists(dbffile))
dbaseFile = new DbaseReader(dbffile);
//Parse shape header
ParseHeader();
//Read projection file
ParseProjection();
}
/// <summary>
/// Opens the datasource
/// </summary>
public void Open()
{
// TODO:
// Get a Connector. The connector returned is guaranteed to be connected and ready to go.
// Pooling.Connector connector = Pooling.ConnectorPool.ConnectorPoolManager.RequestConnector(this,true);
if (!_IsOpen)
{
fsShapeIndex = new FileStream(_Filename.Remove(_Filename.Length - 4, 4) + ".shx", FileMode.Open, FileAccess.Read);
brShapeIndex = new BinaryReader(fsShapeIndex, System.Text.Encoding.Unicode);
fsShapeFile = new FileStream(_Filename, FileMode.Open, FileAccess.Read);
brShapeFile = new BinaryReader(fsShapeFile);
InitializeShape(_Filename, _FileBasedIndex);
if (dbaseFile != null)
dbaseFile.Open();
_IsOpen = true;
}
}
/// <summary>
/// Closes the datasource
/// </summary>
public void Close()
{
if (!disposed)
{
//TODO: (ConnectionPooling)
/* if (connector != null)
{ Pooling.ConnectorPool.ConnectorPoolManager.Release...()
}*/
if (_IsOpen)
{
brShapeFile.Close();
fsShapeFile.Close();
brShapeIndex.Close();
fsShapeIndex.Close();
if (dbaseFile != null)
dbaseFile.Close();
_IsOpen = false;
}
}
}
private void InitializeShape(string filename, bool FileBasedIndex)
{
if (!File.Exists(filename))
throw new FileNotFoundException(String.Format("Could not find file \"{0}\"", filename));
if (!filename.ToLower().EndsWith(".shp"))
throw (new System.Exception("Invalid shapefile filename: " + filename));
LoadSpatialIndex(FileBasedIndex); //Load spatial index
}
private SharpMap.CoordinateSystems.ICoordinateSystem _CoordinateSystem;
/// <summary>
/// Gets or sets the coordinate system of the ShapeFile. If a shapefile has
/// a corresponding [filename].prj file containing a Well-Known Text
/// description of the coordinate system this will automatically be read.
/// If this is not the case, the coordinate system will default to null.
/// </summary>
/// <exception cref="ApplicationException">An exception is thrown if the coordinate system is read from file.</exception>
public SharpMap.CoordinateSystems.ICoordinateSystem CoordinateSystem
{
get { return _CoordinateSystem; }
set {
if (_CoordsysReadFromFile)
throw new ApplicationException("Coordinate system is specified in projection file and is read only");
_CoordinateSystem = value; }
}
/// <summary>
/// Returns true if the datasource is currently open
/// </summary>
public bool IsOpen
{
get { return _IsOpen; }
}
/// <summary>
/// Gets the <see cref="SharpMap.Data.Providers.ShapeType">shape geometry type</see> in this shapefile.
/// </summary>
/// <remarks>
/// The property isn't set until the first time the datasource has been opened,
/// and will throw an exception if this property has been called since initialization.
/// <para>All the non-Null shapes in a shapefile are required to be of the same shape
/// type.</para>
/// </remarks>
public ShapeType ShapeType
{
get {
return _ShapeType; }
}
/// <summary>
/// Gets or sets the filename of the shapefile
/// </summary>
/// <remarks>If the filename changes, indexes will be rebuilt</remarks>
public string Filename
{
get { return _Filename; }
set {
if (value != _Filename)
{
_Filename = value;
if (this.IsOpen)
throw new ApplicationException("Cannot change filename while datasource is open");
ParseHeader();
ParseProjection();
tree = null;
}
}
}
/// <summary>
/// Gets or sets the encoding used for parsing strings from the DBase DBF file.
/// </summary>
/// <remarks>
/// The DBase default encoding is <see cref="System.Text.Encoding.UTF7"/>.
/// </remarks>
public System.Text.Encoding Encoding
{
get { return dbaseFile.Encoding; }
set { dbaseFile.Encoding = value; }
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -