📄 mifreader.cs
字号:
using System;
using System.IO;
using System.Text.RegularExpressions;
using GeoCon.Data;
using gml;
namespace GeoCon.mif
{
/// <summary>
/// Reader for MapInfo mif/mid file.
/// </summary>
public class MifReader
{
/// <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);
#region private constants
private const string TYPE_NONE = "none";
private const string TYPE_POINT = "point";
private const string TYPE_LINE = "line";
private const string TYPE_PLINE = "pline";
private const string TYPE_REGION = "region";
private const string TYPE_ARC = "arc";
private const string TYPE_TEXT = "text";
private const string TYPE_RECT = "rect";
private const string TYPE_RECT2 = "rectangle";
private const string TYPE_ROUNDRECT = "roundrect";
private const string TYPE_ROUNDRECT2 = "rounded";
private const string TYPE_ELLIPSE = "ellipse";
private const string CLAUSE_SYMBOL = "SYMBOL";
private const string CLAUSE_PEN = "PEN";
private const string CLAUSE_SMOOTH= "SMOOTH";
private const string CLAUSE_CENTER = "CENTER";
private const string CLAUSE_BRUSH = "BRUSH";
private const string CLAUSE_VERSION = "VERSION";
private const string CLAUSE_CHARSET = "CHARSET";
private const string CLAUSE_DELIMETER = "DELIMITER";
private const string CLAUSE_COORDSYS = "COORDSYS";
private const string CLAUSE_UNIQUE = "UNIQUE";
private const string CLAUSE_INDEX = "INDEX";
private const string CLAUSE_COLUMNS = "COLUMNS";
#endregion
#region private fields
private System.IO.StreamReader fReader;
private System.IO.StreamReader mReader;
private string name;
//private string defaultsrs="http://www.opengis.net/gml/srs/epsg.xml#4326";
private string defaultsrs=" ";
private bool hasBBox=false; //whether bounding box supplied in mif file
private mif.FeatureHeader fHeader;
private util.StringTokenizer tokenizer = new util.StringTokenizer("");
#endregion
#region constructor & public methods
/// <summary>
/// Construct new MifReader.
/// </summary>
/// <param name="mifpath">path to the mif file.</param>
public MifReader(string mifpath)
{
try
{
fHeader = new FeatureHeader();
System.IO.Stream fstream = new FileStream(mifpath,FileMode.Open,FileAccess.Read,FileShare.Read);
fReader = new StreamReader(fstream);
string fne =System.IO.Path.GetFileNameWithoutExtension(mifpath);
name=fne;
string midpath = fne + ".mid";
if(File.Exists(midpath))
{
System.IO.Stream dstream = new FileStream(midpath,FileMode.Open,FileAccess.Read,FileShare.Read);
mReader = new StreamReader(dstream);
}
}
catch (System.IO.FileNotFoundException fnfe)
{
//System.Windows.Forms.MessageBox.Show("file '"+fnfe.FileName+"' not found.");
throw new FileNotFoundException("MifReader : "+fnfe.FileName + " not found.\n" + fnfe.Message);
}
catch(System.Exception ioe)
{
//System.Windows.Forms.MessageBox.Show("error : \r"+ioe.Message);
throw new Exception("MifReader : "+ioe.Message,ioe.InnerException);
}
}
/// <summary>
/// Starts reading shape and data
/// </summary>
/// <returns>new MapData which holds the mif/mid data.</returns>
public MapData Read()
{
UpdateStatus("reading "+name+"..",0);
//initializing box values, in case bounding box not supplied by mif file;
fHeader.coordSys.left = double.MaxValue;
fHeader.coordSys.top = -double.MaxValue;
fHeader.coordSys.right = -double.MaxValue;
fHeader.coordSys.bottom= double.MaxValue;
MapData mapdata = new MapData();
mapdata.Name=name;
ReadHeader(ref mapdata);
ReadData(ref mapdata);
mapdata.Info.FileDirectory = System.Environment.CurrentDirectory;// System.IO.Path.GetDirectoryName(name);
mapdata.Info.FileVersion = fHeader.version.ToString();
mapdata.Info.FileGeometry = name+".mif";
mapdata.Info.FileData = (mReader!=null)? name+".mid" : "not found";
mapdata.Info.RecordsCount = mapdata.Fields[0].Count;
mapdata.Info.ProjectionName = fHeader.coordSys.Name;
mapdata.Info.BoundsLeft=fHeader.coordSys.left;
mapdata.Info.BoundsTop=fHeader.coordSys.top;
mapdata.Info.BoundsRight=fHeader.coordSys.right;
mapdata.Info.BoundsBottom=fHeader.coordSys.bottom;
mapdata.BoundingBox=new gml.BoxType(fHeader.coordSys.left,fHeader.coordSys.top,fHeader.coordSys.right,fHeader.coordSys.bottom);
UpdateStatus("Finished reading "+name,0);
return mapdata;
}
/// <summary>
/// close the reader/stream associated with this reader. Plus some clean up.
/// </summary>
public void Close()
{
fReader.Close();
if(mReader!=null) mReader.Close();
name=null;
defaultsrs=null;
tokenizer=null;
}
#endregion
#region header stuff
/// <summary>
/// Read mif header information.
/// </summary>
/// <param name="mapdata">MapData into which the data will be stored</param>
private void ReadHeader(ref MapData mapdata)
{
//onStatusChange(new GeoCon.Util.StatusEventArgs("reading header..",0));
string[] uniques=new string[0]; //unique columns indexes
string[] indexes=new string[0]; //indexed columns indexes
string token="";
util.StringTokenizer stoken = new util.StringTokenizer(" ");
while(fReader.Peek()>=0)
{
stoken.str=fReader.ReadLine();
stoken.reset();
if(stoken.hasMoreTokens()) token=stoken.nextToken();
if(token.ToUpper()=="DATA") break;
switch (token.ToUpper())
{
case CLAUSE_VERSION :
if(stoken.hasMoreTokens()) fHeader.version=double.Parse(stoken.nextToken().Trim());
break;
case CLAUSE_CHARSET :
if(stoken.hasMoreTokens()) fHeader.charset=stoken.nextToken().Trim(new char[1]{'"'});
break;
case CLAUSE_DELIMETER : //delimiter for data in mid file
if(stoken.hasMoreTokens()) fHeader.delimiter=stoken.nextToken().Trim(new char[1]{'"'});
break;
case CLAUSE_COORDSYS :
stoken.str=stoken.str.Replace(fHeader.delimiter+" ", fHeader.delimiter);
ReadCoordSys(stoken);
break;
case CLAUSE_UNIQUE :
if(stoken.hasMoreTokens()) uniques=stoken.nextToken().Trim().Split(fHeader.delimiter.ToCharArray());
break;
case CLAUSE_INDEX :
if(stoken.hasMoreTokens()) indexes=stoken.nextToken().Trim().Split(fHeader.delimiter.ToCharArray());
break;
case CLAUSE_COLUMNS :
//onStatusChange(new util.StatusEventArgs("reading columns..",0));
int count=0;
if(stoken.hasMoreTokens()) count = Convert.ToInt32(stoken.nextToken());
ReadColumnHeader(ref mapdata,count);
break;
default:
// unknown/empty line identifier
break;
}
}
token=null;
stoken=null;
for(int i=0;i<uniques.Length;i++)
{
int fi=int.Parse(uniques[i])-1;
mapdata.Fields[fi].isUnique=true;
}
for(int i=0;i<indexes.Length;i++)
{
int fi=int.Parse(indexes[i])-1;
mapdata.Fields[fi].isIndex=true;
}
uniques=null;
indexes=null;
}
/// <summary>
/// Reads column information in a mif file.
/// </summary>
/// <param name="mapdata">MapData where the column info will be stored</param>
/// <param name="count">number of columns to read</param>
private void ReadColumnHeader(ref MapData mapdata,int count)
{
Field f;
string tp="";
//util.StringTokenizer st=new util.StringTokenizer(""," (),\0".ToCharArray());
util.StringTokenizer st=new util.StringTokenizer(""," ,()\0".ToCharArray());
for (int i=0;i<count;i++)
{
st.reset();
st.str=fReader.ReadLine();
if(mReader==null) continue;
mapdata.Info.FieldsCount+=1;
string fname="";
if(st.hasMoreTokens()) fname = st.nextToken();
if(st.hasMoreTokens()) tp = st.nextToken();
switch (tp.ToLower())
{
case "char":
f = st.hasMoreTokens()? new StringField(int.Parse(st.nextToken())) : new StringField();
break;
case "integer":
f=new IntegerField();
break;
case "smallint":
f=new IntegerField();
break;
case "float":
f=new DoubleField();
break;
case "decimal":
f=new DoubleField(int.Parse(st.nextToken()),int.Parse(st.nextToken()));
break;
case "date":
f=new DateTimeField();
break;
case "logical":
f=new BoolField();
break;
default :
f = new StringField();
break;
}
f.Name = fname;
mapdata.Fields.Add(f);
}
f=null;
st=null;
//mapdata.Info.FieldsCount = count;
}
#endregion
#region read data stuff
/// <summary>
/// Reads geometry & non-geometry data.
/// </summary>
/// <param name="mapdata">MapData into where the data will be stored</param>
private void ReadData(ref MapData mapdata)
{
int curcount=0;
GeometryField fgeo=new Data.GeometryField();
string fpartType="";
while(fReader.Peek()>=0)
{
curcount+=1;
if(curcount>=99) curcount=0;
UpdateStatus("",curcount);
tokenizer.reset();
tokenizer.str = fReader.ReadLine();
tokenizer.delimiters =new char[1]{' '};
if(tokenizer.hasMoreTokens()) fpartType=tokenizer.nextToken();
switch (fpartType.ToLower())
{
case TYPE_NONE :
ReadFeatureNone(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_POINT :
ReadFeaturePoint(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_LINE :
ReadFeatureLine(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_PLINE :
ReadFeaturePLine(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_REGION :
ReadFeatureRegion(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_ARC :
ReadFeatureArc(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_TEXT :
ReadFeatureText(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_RECT :
ReadFeatureRect(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_RECT2 : //older version of rectangle
ReadFeatureRect(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_ROUNDRECT :
ReadFeatureRoundRect(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_ROUNDRECT2 : //older version of rounded rectangle
ReadFeatureRoundRect(ref fgeo);
ReadFeatureData(ref mapdata);
break;
case TYPE_ELLIPSE :
ReadFeatureEllipse(ref fgeo);
ReadFeatureData(ref mapdata);
break;
default:
curcount-=1;
//check if this is style data
if(isShadingClause(tokenizer.str)) ReadFeatureStyle(tokenizer.str);
break;
}
}
mapdata.Fields.Add(fgeo);
}
#endregion
#region read geometry stuff
/// <summary>
/// Read null shape geometry.
/// </summary>
/// <param name="fgeo">The GeometryField where the new feature will be added to</param>
private void ReadFeatureNone(ref GeometryField fgeo)
{
fgeo.Add(fgeo.NullSymbol);
}
/// <summary>
/// Read feature text
/// </summary>
/// <param name="fgeo">The GeometryField where the new feature will be stored</param>
private void ReadFeatureText(ref GeometryField fgeo)
{
//consider this rect as the text background rectangle :)
fReader.ReadLine(); //skip one line -> this is the text value;
tokenizer.reset();
tokenizer.str=fReader.ReadLine(); //this is the bounding box
ReadFeatureRect(ref fgeo);
}
/// <summary>
/// Read Arc
/// </summary>
/// <param name="fgeo">The GeometryField to store the new gml type</param>
private void ReadFeatureArc(ref GeometryField fgeo)
{
//i know i should turn arcs into polylines, but why bother?
//i'd rather have other people do the conversion using mapinfo
ReadFeatureRect(ref fgeo);
}
/// <summary>
/// Read Ellipse
/// </summary>
/// <param name="fgeo">The GeometryField to store the new gml type</param>
private void ReadFeatureEllipse(ref GeometryField fgeo)
{
//idem
ReadFeatureRect(ref fgeo);
}
/// <summary>
/// Read Rounded Rectangle
/// </summary>
/// <param name="fgeo">The GeometryField to store the new gml type</param>
private void ReadFeatureRoundRect(ref GeometryField fgeo)
{
ReadFeatureRect(ref fgeo);
//just the rectangle.
}
/// <summary>
/// Read Rectangle
/// </summary>
/// <param name="fgeo">The GeometryField to store the new gml type</param>
private void ReadFeatureRect(ref GeometryField fgeo)
{
double left = double.Parse(tokenizer.nextToken());
double bottom = double.Parse(tokenizer.nextToken());
double right = double.Parse(tokenizer.nextToken());
double top = double.Parse(tokenizer.nextToken());
//update bbox
if(hasBBox==false) updateBoundingBox(left,top,right,bottom);
double[] da=new double[8]{left,bottom, left,top, right,top, right,bottom};
gml.PolygonType _pt=new PolygonType(new gml.CoordinatesType(da));
fgeo.Add(new gml.PolygonPropertyType(_pt));
da=null;
}
/// <summary>
/// Read point
/// </summary>
/// <param name="fgeo">The GeometryField to store the new gml type</param>
private void ReadFeaturePoint(ref GeometryField fgeo)
{
int tt=-1;
double[] da=new double[2];
while(tokenizer.hasMoreTokens())
{
tt++;
da[tt] = double.Parse(tokenizer.nextToken());
if(tt>1) break;
}
if(hasBBox==false) updateBoundingBox(da[0],da[1],da[0],da[1]);
gml.PointType _pt = new gml.PointType(new gml.CoordinatesType(da));
fgeo.Add(new gml.PointPropertyType(_pt));
da=null;
_pt=null;
}
/// <summary>
/// Read Line
/// </summary>
/// <param name="fgeo">The GeometryField to store the new gml type</param>
private void ReadFeatureLine(ref GeometryField fgeo)
{
double[] da=new double[4];
int tt=-1;
while(tokenizer.hasMoreTokens())
{
tt++;
da[tt] = double.Parse(tokenizer.nextToken());
if(tt>3) break;
}
if(hasBBox==false)
{
updateBoundingBox(da[0],da[1],da[2],da[3]);
updateBoundingBox(da[2],da[3],da[0],da[1]);
}
gml.LineStringType _ls = new gml.LineStringType(new gml.CoordinatesType(da));
fgeo.Add(new gml.LineStringPropertyType(_ls));
da = null;
_ls = null;
}
/// <summary>
/// Reads Polyline
/// </summary>
/// <param name="fgeo">The GeometryField to store the new gml type</param>
private void ReadFeaturePLine(ref GeometryField fgeo)
{
string token = tokenizer.nextToken();
if(token.ToLower()=="multiple")
{
int partsNum=int.Parse(tokenizer.nextToken());
gml.LineStringType[] LSArray = new gml.LineStringType[partsNum];
for(int j=0;j<partsNum;j++)
{
int tuplesNum = int.Parse(fReader.ReadLine());
LSArray[j] = new gml.LineStringType(new gml.CoordinatesType(ReadCoordinates(tuplesNum)));
}
gml.MultiLineStringType _mls = new gml.MultiLineStringType(defaultsrs,LSArray);
fgeo.Add(new gml.MultiLineStringPropertyType(_mls));
_mls=null;
LSArray=null;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -