当前位置: 代码迷 >> GIS >> gis试用期答题手记
  详细解决方案

gis试用期答题手记

热度:160   发布时间:2016-05-05 06:28:24.0
gis试用期解题手记
题目1: 生成内外多边形
类型: 招聘应试题目/试用期考核题/实习期练习题
等级: B
时间: 2个工作日
语言: 自选
条件: 1、从屏幕上通过鼠标随机输入一个多边形。(如下图 黑色 部分, 任意形状,但没有自相交部份)
   2、通过对话框输入外垂距A和内垂距B。
结果: 生成与输入多边形平行的二多边形(如下图 红色和蓝色 部分);
  与输入线的距离分别为已输入的垂距。
示例:

题目2: 生成缓冲区
类型: 招聘应试题目/试用期考核题/实习期练习题
等级: B
时间: 2 个工作日
语言: 自选
条件: 1、从屏幕上通过鼠标随机输入2条线段,可相交,也可不相交。
   2、通过对话框输入1个垂距。
   3、2条线的夹角为0度至180度。
   
结果: 生成缓冲区(到所绘线距离小于给定垂距的所有点集)多边形,做一开关,可控制填色与不填色。
  多边形边线与已输入线的距离为输入的垂距。
示例:

题目3: 读取地图文件
类型: 招聘应试题目/试用期考核题/实习期练习题
等级: C(不读DBF条件 1~4)
    B1(读DBF条件进行标注,不考虑避让条件 1~5)
    B2(实现查询 1~4,6)
    A  (实现查询,读DBF文件进行标注,不考虑避让条件1~6)
    S(实现查询,读DBF进行标注,且实现标注的避让 条件1~7)
时间: 3/5/6/7个工作日
语言: 自选
条件: 1、地图文件格式文档了解文件结构;
   2、通过编程读取指定文件组:如rivers的数据读取;
   3、通过编程把读取的数据绘制在自编应用程序的界面上;
   4、可通过鼠标右键或滚轮对鼠标选定位置进行放大,放大比例可调;
5、读取DBF对地图上的线进行标注,当标注内容为英文有中间有空格或标注为中文字符时,标注需要的每个词/字应保持一定距离(B/A/S级)
6、.鼠标左键点击地图上的线时,对该线进行查询,显示该线所对应的所有属性信息。(B/A/S级)
7、当标注文字相互间发生相互重叠时,将标注文字调整到非重叠且无歧义的位置。(S级)
   
结果预览:





已知两点AB,画线段
MoveTo(A);
LineTo(B);

重载鼠标左键方法:
BEGIN_MESSAGE_MAP(CDrawPolygonView, CView)
//{{AFX_MSG_MAP(CTView)
ON_WM_LBUTTONDOWN() // 重载鼠标左键
ON_WM_RBUTTONDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()




自定义按钮生成的类中如何获取数据
void PolygonIN::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT1, m_Text);
}

获取当前窗口和工作区范围的函数
//取得当前窗口的右下角坐标
CRect   rt;  
//GetWindowRect(&rt);  //取得当前窗口的矩形
GetClientRect(&rt);  //取得当前工作区的矩形
CPoint   pt;  
pt   =   rt.BottomRight();



解题思路:

题目一:
重载鼠标左键,记录当前鼠标点击点坐标,并画多边行。
根据输入垂距计算内外多边形。
计算线段AB中A点和B点对应垂距的坐标点


先计算A1,A0
方法,A1B1 AB A0B0平行,AB与X轴夹角的tan值得绝对值是相等的。得方程一:
Tan∠O = (Yb - Ya)/(Xb - Xa) = (Xa1 – Xa )/(Ya1 - Ya )

点A1A的距离为X,得方程二:
Pow(X,2) = pow((Xa1 - Xa),2)+pow((Ya1-Ya),2);

解方程可以得到
|Xa1 – Xa| = sqrt(pow(X,2) * pow((Yb – Ya),2)/(pow((Xb-Xa),2)*pow(Yb-Ya),2));
|Ya1 – Ya| = sqrt(pow(X,2) * pow((Xb – Xa),2)/(pow((Xb-Xa),2)*pow(Yb-Ya),2))

此时得到XOffset和YOffset。
则可以得到4个点 (Xa+XOffset, Ya+YOffset), (Xa+XOffset, Ya-YOffset),
(Xa-XOffset, Ya+YOffset), (Xa-XOffset, Ya-YOffset)

判断两个线段是否垂直
向量AB * 向量CD = |AB| * |CD| * cos∠O = (Xb – Xa)*(Xd - Xc) + (Yb - Ya)*(Yd - Yc)
∠O为两条向量的夹角
cos90° = 0;

此时可以得到点A1和A0 。
需要判断这两个点中,哪个点属于外多边的,哪个点属于内多边形。
多边形是顺时针构成的,则线段AB的左边的点为外多边的点。
多边形是逆时针构成的,则线段AB的右边的点位内多边的点。
// 平面上的三点P1(x1,y1),P2(x2,y2),P3(x3,y3)的面积量:S(P1,P2,P3)=(x1-x3)*(y2-y3)-(y1-y3)*(x2-x3)
// 如果S(P1,P2,P3)<0,则P1P2P3是顺指针的
// 如果S(P1,P2,P3)>0,则P1P2P3是逆指针的
// 如果S(P1,P2,P3)=0,则P1P2P3三点共线

此时可以得到内多边形每条边对应得线段和外多边形对应得线段。
计算相邻两条线段的交点,可以得到所有顶点坐标。

//直线方程:x+k*y-b=0
//已知直线两点A(x1,y1)B(x2,y2)
则k = (x2-x1)/(y1-y2);
  b = x1+k*y1;
//已知直线方程:x+k1*y-b1=0和x+k2*y-b2=0
则y = (b1-b2)/(k1-k2)
  X = b1-k1*(b1-b2)/(k1-k2)

对应我们的程序,已知两条连续线段的顶点,则可以得到其交点。
//由于是多边形,不存在连续两条边平行,即k1=k2
//也不存在两条连续都垂直于y轴的情况
// 需要考虑的条件有:
两条直线都不垂直于y轴; b>其中一条直线垂直于y轴。


至此,可以得到所有内外多边形顶点的坐标。


题目二:
偷懒方法:
计算工作区内所有点到所画线段的距离,
若距离 == 垂距,则设置改像素点的颜色为黑色。
WINGDIAPI COLORREF WINAPI SetPixel(__in HDC hdc, __in int x, __in int y, __in COLORREF color);
如果需要上色,则距离小于垂距但大于0.5的像素点设置为红色
这样设置存在一个问题,在线断周围会出现白色的点。因为有些像素点到线段的距离小于0.5,但是又不在线段上面。所有额外再设置距离小于等于0.5的像素点设置为黑色。

计算点c到线段ab的距离
double CBufferGeneratorView::DistanceLine(CPoint a, CPoint b, CPoint c)  // a和b是线段的两个端点,c是检测点

{
CPoint ab = b - a;
CPoint ac = c - a;
//float f = ab * ac;
double f = (b.x-a.x)*(c.x-a.x)+(b.y-a.y)*(c.y-a.y);
//c到直线ab的投影点不在线段ab上,而且离a点最近
if (f<0) return Distance(a, c);
//float d = ab*ab;
double d = (b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y);
//c到直线ab的投影点不在线段ab上,而且离b点最近
if ( f>d) return Distance(b, c);
// c在ab线段上的投影点在线段ab上
f = f/d;
//CPoint D = a + f *ab;
//CPoint D;
//D.x = a.x + f*(b.x-a.x);
//D.y = a.y + f*(b.y-a.y);
//   return Distance(c, D);
double fDx = a.x + f * (b.x-a.x);
double fDy = a.y + f * (b.y-a.y);
double fDistanceCD = sqrt(pow(static_cast<double>(c.x-fDx),2) + pow(static_cast<double>(c.y-fDy),2));
return fDistanceCD;
}


题目三:
不涉及特别的算法
具体要求具体实现
解析每个Element(河流)的parts,读取每个parts包含的顶点坐标,连续画线。

存在一个坐标转换得问题。
需要把地图上每个点的坐标转换到屏幕对应的点。
第一步,坐标原点的确定。
每次放大缩小,都是以屏幕某一点为中心进行放大缩小。



原始地图坐标系为图A,
p0:x0 = 0, y0 = 0;
p1:x1 = x1, y1 = y1;
图中任一点坐标p2:x2=x2,y2=y2

以(x1,y1)为坐标原点构建新坐标系图B,则地图中各点坐标为
p0:x0 =0 - x1, y0 = 0 - y1;
p1:x1 = 0, y1 = 0;
图中任一点坐标p2:x2=x2-x1,y2=y2-y1


转换到屏幕工作区。图C
屏幕工作区坐标系于传统坐标系有所不同,以左上角作为坐标原点(0,0)。X轴向右逐渐增大,y轴向下逐渐增大。
p0:x0 =0 - x1, y0 = -(0 - y1);
p1:x1 = 0, y1 = 0;
图中任一点坐标p2:x2=x2-x1,y2=-(y2-y1)


把地图坐标原点从左上角移到中间点。坐标系原点依然是工作区左上角。工作区右下角点为pt。则
p0:x0 =(0 - x1) + 0.5*pt.x, y0 = -(0 - y1)+ 0.5*pt.y;
p1:x1 = 0, y1 = 0;
图中任一点坐标p2:x2=(x2-x1)+ 0.5*pt.x,y2 = -(y2-y1) + 0.5*pt.y


增加缩放比例k。则放大k倍后地图坐标
p2: x2= k*(x2-x1) + 0.5*pt.x;y2 = -k*(y2-y1) + 0.5pt.y

根据以上计算,则地图任一点p(x,y),在地图以点m_cPointZero为中心放大m_fScale倍后,对应屏幕工作区的坐标为
cPointTmp1.x = static_cast<long>(0.5*(pt.x)+m_fScale*(x-m_cPointZero.x));
cPointTmp1.y = static_cast<long>(0.5*(pt.y)+m_fScale*(m_cPointZero.y-y));

屏幕任一点point,对应地图坐标为:
m_cPointZero.x = (point.x-0.5*(pt.x))/m_fScale+m_cPointZero.x;
m_cPointZero.y = m_cPointZero.y - (point.y-0.5*(pt.y))/m_fScale;
  相关解决方案