陈丽金
(福建东辰综合勘察院,福建 福州 350001)
地籍调查是依照国家相关法律法规,通过地籍测绘和权属调查,确定宗地的权属状态、位置坐落、界址分布、宗地面积、宗地用途等信息,并形成一系列调查表格、图件、数据库等信息成果,为土地登记和发证提供依据的一项重要基础工作[1]。宗地是地籍调查的基本单元,是指由封闭的权属线所包围的地块或空间,构成宗地的边界线称为界址线,而界址线的转折点称为界址点,因此可以说,界址点、界址线唯一确定了宗地的空间位置[2]。
在地籍调查中,地籍图和地籍调查表是两类非常重要的成果,地籍图是以特定的投影、比例、符号系统将地籍要素表示在地图中,反映各宗地的位置分布和空间关系的图件,CASS是地籍调查中应用非常广泛的制图软件[3]。地籍调查表是以表格的形式记录宗地相关属性信息的载体,由封面、宗地基本信息表、界址信息表等部分构成,其中,界址信息表中记录界址点号、界址间距等界址描述信息[4]。由于地籍图中已经记录了宗地各界址点的坐标,因此可以利用地籍图中的相关信息自动生成界址信息表。本文将讨论CASS软件绘制的地籍图中批量转出界址信息的方法。
CASS地籍图中宗地、界址线、界址点通过宗地代码进行关联,对于同一宗地,其界址点编号自宗地西北角从1开始顺时针进行编号,如图1所示。
图1 地籍图中的界址点编号
CASS提供了两种方式批量转出界址信息,一种是通过“输出宗地属性”工具将所有宗地的权属信息转出到mdb数据库中,该数据库包含zdinfo、jzdinfo、jzxinfo三个表,分别存储宗地、界址点、界址线属性信息。在jzxinfo表中QDH和ZDH分别记录界址线的起止界址点点号,然而该编号是以地籍子区为单位而不是以宗地为单位进行顺序编号,与图面表达不一致,如图2所示。
图2 CASS中批量转出的jzxinfo表的界址点号
另一种方式是通过“绘制地籍表格”菜单组中的“界址点成果表(excel)”导出界址信息,以excel方式存储,每宗地的界址信息存储在一个表单(sheet)中,如图3所示。该种方式中的界址点号仍以地籍子区为单位进行顺序编号。此外,如果宗地数量较多,该种方式导出界址信息时软件容易崩溃,无法将所有宗地的界址信息导出。
图3 CASS中批量转出的界址点成果表
上述两种方式转出的界址信息的共同点是界址点无法按宗地进行从1开始编号,本文探索一种基于CASS中权属文件的界址信息批量转出方式。
首先,利用CASS软件中的“权属文件生成”菜单组中的“由界址线生成”,指定一条闭合线,可将内部所有宗地的界址信息导出到外部后缀格式为.qs的文本文件中,用记事本打开该文件,内容格式如图4所示。文件中,每宗地包含一组信息,第一行为宗地代码,第二行为权利人名称,第三行为地类代码,第四行开始每三行为一个界址点信息,包含点号和横纵坐标值,宗地信息以“E,宗地面积”作为最后一行,然后继续下一宗地的信息组,直到最后一宗地,再以“E”结束权属文件。
图4 权属文件格式
权属文件中,界址点以地籍子区为单位从小到大编号,对于单宗地,从西北角开始按顺时针方向编号。例如,某地籍子区第一宗地的界址点编号为J1-J5,第二宗地的界址点号为J6-J12,以此类推。在地籍调查表中,每一宗地的界址点编号一般从西北角开始顺时针从1开始编号,因此,需要对上述权属文件(.qs)进行解析处理,对每宗地的界址点重新进行编号即可。算法步骤如下:
(1)读取权属文件,将所有行的内容存储在行信息数组中。
(2)创建宗地信息动态数组,数组元素为宗地信息,宗地信息元素包含宗地代码属性和界址点动态数组,界址点数组元素包含界址点x,y坐标值。
(3)遍历行信息数组,读取并记录第一行宗地代码,存入当前宗地元素的宗地代码属性。
(4)跳3行读取原始界址点号,如果不是以“E,”开头,则记录下面2行的坐标值,存入当前宗地元素界址点数组。
(5)重复执行步骤(4),直到遇到“E,”,说明本宗地信息组结束。转入步骤(3),继续读取下一宗地信息。
(6)重复执行步骤(3)到步骤(5),直到所有行信息读取结束。
(7)创建一个csv文件用于存储界址信息,每行用于存储一条界址线信息,包括宗地代码、起始界址点号、终止界址点号、界址线长度四个字段信息。
(8)遍历上述宗地信息动态数组,读取每一宗地的宗地代码属性和界址点动态数组,界址点编号从1开始续编,相邻两个界址点构成一条界址线(最后一个点与第一个点闭合),计算其直线距离即为界址线长度,存入界址信息文件。
(9)直到宗地信息动态数组遍历结束,关闭界址信息文件,转换结束。
用C#语言对上述算法进行编程实现,核心代码如下所示:
private void ConvertQS(string qsFile,string result)
{
string zddm = "";
string[]lines = File.ReadAllLines(qsFile, Encoding.Default);//行信息数组
lines = lines.Take(lines.Length-1).ToArray();//去掉最后一行结束标记(E)
int line_count = lines.Length;//行数
int lineIndex = 0;//行序号
List
while(lineIndex < line_count)//遍历行信息数组
{
zddm = lines[lineIndex];//第一行为宗地代码
ZDInfo zdInfo = new ZDInfo(zddm);
lineIndex += 3;//转到界址点记录处
string jzp_no = lines[lineIndex];//原始界址点点号
//未遇到E,表示本宗地界址点未遍历结束,记录本界址点信息
while(!jzp_no.StartsWith("E,"))
{
string x = lines[lineIndex + 1];
string y = lines[lineIndex + 2];
zdInfo.jzs.Add(new JZInfo(x, y));//当前界址点存入宗地信息
lineIndex += 3;//每个点有3行信息
jzp_no = lines[lineIndex];//继续遍历本宗地界址点
}
zdInfos.Add(zdInfo)//本宗地遍历结束,存入宗地信息数组
lineIndex++;//转入下一宗地
}
//遍历宗地信息动态数组进行界址点重编号
string[]contents = zdInfos.OrderBy(item => item.zddm).ToList().Select(zdInfo =>
{
StringBuilder sb = new StringBuilder();
//遍历当前宗地界址点动态数组,相邻两点构成一条界址线(最后一点与起点闭合)
for(int i = 1, len = zdInfo.jzs.Count; i <= len; i++)//对每一宗地的界址点从1开始序编
{
string start = string.Format("J{0}", i);
string end = i != len ? string.Format("J{0}", i + 1): "J1";
//计算两点之间的直线距离
JZInfo startP = zdInfo.jzs[i-1];
JZInfo endP = zdInfo.jzs[i != len ? i : 0];
string dis = Math.Sqrt(Math.Pow(startP.x-endP.x, 2)+ Math.Pow(startP.y-endP.y, 2)).ToString("F3");
sb.AppendLine(string.Format("{0},{1},{2},{3}", zdInfo.zddm, start, end, dis));
}
return sb.ToString().TrimEnd(new char[]{ ‘ ’, ‘ ’ });
}).ToArray();
//写入结果文件
File.WriteAllText(result, "宗地代码,起始点号,终止点号,界址线长度 " + string.Join(" ", contents), Encoding.Default);
}
运行上述代码对原始权属文件进行处理,得到如图5所示结果,可以看出,每一宗地的界址点号已从1开始编码,并自动计算得到每条界址线的长度。在地籍图中对宗地原始的界址点号和界址线的长度
图5 转换结果
进行了比对,转换结果中相邻界址点的编号保持了原有的相对关系,且界址线长度也与地籍图上一致,验证了该方法的可行性。在该结果的基础上可以很容易地进行地籍调查表中界址信息的格式化输出以及数据入库等其他转化,本文不做深入阐述。