[SharpMap] 屏幕坐标和Map坐标转换
1. SharpMap中屏幕坐标和地图Map坐标转换:
using System.Drawing;
using GeoAPI.Geometries; namespace SharpMap.Utilities
{
/// <summary>
/// Class for transforming between world and image coordinate
/// </summary>
public class Transform
{
/// <summary>
/// Transforms from world coordinate system (WCS) to image coordinates
/// 将世界坐标转换为image坐标
/// NOTE: This method DOES NOT take the MapTransform property into account (use <see cref="Map.WorldToImage(GeoAPI.Geometries.Coordinate,bool)"/> instead)
/// </summary>
/// <param name="p">Point in WCS</param>
/// <param name="map">Map reference</param>
/// <returns>Point in image coordinates</returns>
public static PointF WorldtoMap(Coordinate p, Map map)
{
//if (map.MapTransform != null && !map.MapTransform.IsIdentity)
// map.MapTransform.TransformPoints(new System.Drawing.PointF[] { p });
if (p.IsEmpty())
return PointF.Empty; var result = new PointF(); var height = (map.Zoom * map.Size.Height) / map.Size.Width;
var left = map.Center.X - map.Zoom * 0.5;
var top = map.Center.Y + height * 0.5 * map.PixelAspectRatio;
result.X = (float)((p.X - left) / map.PixelWidth);
result.Y = (float)((top - p.Y) / map.PixelHeight);
if (double.IsNaN(result.X) || double.IsNaN(result.Y))
result = PointF.Empty;
return result;
} /// <summary>
/// Transforms from image coordinates to world coordinate system (WCS).
/// NOTE: This method DOES NOT take the MapTransform property into account (use <see cref="Map.ImageToWorld(System.Drawing.PointF,bool)"/> instead)
/// </summary>
/// <param name="p">Point in image coordinate system</param>
/// <param name="map">Map reference</param>
/// <returns>Point in WCS</returns>
public static Coordinate MapToWorld(PointF p, Map map)
{
if (map.Center.IsEmpty() || double.IsNaN(map.MapHeight))
{
return new Coordinate(, );
}
var ul = new Coordinate(map.Center.X - map.Zoom * ., map.Center.Y + map.MapHeight * .);
return new Coordinate(ul.X + p.X * map.PixelWidth,
ul.Y - p.Y * map.PixelHeight);
}
}
}
Transform
详细分析: http://www.cnblogs.com/yhlx125/archive/2012/02/10/2342282.html
2. OpenS-CAD中的实现
已知屏幕分辨率每英寸像素点数,一般为96dpi, 定义float m_screenResolution = 96;
1. 初始化CanvasCtrl时,首先调用OnResize()方法。
protected override void OnResize(EventArgs e)
{
base.OnResize(e); if (m_lastCenterPoint != UnitPoint.Empty && Width != )
SetCenterScreen(ToScreen(m_lastCenterPoint), false);
m_lastCenterPoint = CenterPointUnit();
m_staticImage = null;
DoInvalidate(true);
}
OnResize
由于m_lastCenterPoint是结构体变量,所以首先设置到中心点m_lastCenterPoint,即(0,0),是Unit坐标
if (m_lastCenterPoint != UnitPoint.Empty && Width != 0)
SetCenterScreen(ToScreen(m_lastCenterPoint), false);
接着调用m_lastCenterPoint = CenterPointUnit();
通过直角坐标的左上角点和右下角点计算。其实初始化时候执行该方法没有起到设置基准点的作用。可以跳过这2次,等窗体Resize的时候再看。
public UnitPoint CenterPointUnit()
{
UnitPoint p1 = ScreenTopLeftToUnitPoint();
UnitPoint p2 = ScreenBottomRightToUnitPoint();
UnitPoint center = new UnitPoint();
center.X = (p1.X + p2.X) / ;
center.Y = (p1.Y + p2.Y) / ;
return center;
}
public UnitPoint ScreenTopLeftToUnitPoint()
{
return ToUnit(new PointF(, ));
}
public UnitPoint ScreenBottomRightToUnitPoint()
{
return ToUnit(new PointF(this.ClientRectangle.Width, this.ClientRectangle.Height));
}
CenterPointUnit
2. 接着打开文档DocumentForm,在构造的过程中设置文档画布的视点中心坐标为(0,0)。这样就实现了绘图画布原点坐标和屏幕(客户区)中心点的对应,形成基准点。默认的数据的长度单位为inch,屏幕坐标的单位为像素。这两者之间存在比例关系,通过Zoom缩放比例来实现尺度的变换,同时结合平移量和偏移距离计算出鼠标点的世界坐标。
m_canvas.SetCenter(new UnitPoint(0, 0));
或者加载完数据,设置画布的视点中心坐标。
m_canvas.SetCenter(m_data.CenterPoint);
public DocumentForm(string filename)
{
InitializeComponent(); Text = "<New Document>";
m_data = new DataModel();
if (filename.Length > && File.Exists(filename) && m_data.Load(filename))
{
Text = filename;
m_filename = filename;
} m_canvas = new CanvasCtrl(this, m_data);
m_canvas.Dock = DockStyle.Fill;
Controls.Add(m_canvas);
m_canvas.SetCenter(new UnitPoint(, ));
m_canvas.RunningSnaps = new Type[]
{
typeof(VertextSnapPoint),
typeof(MidpointSnapPoint),
typeof(IntersectSnapPoint),
typeof(QuadrantSnapPoint),
typeof(CenterSnapPoint),
typeof(DivisionSnapPoint),
}; m_canvas.AddQuickSnapType(Keys.N, typeof(NearestSnapPoint));
m_canvas.AddQuickSnapType(Keys.M, typeof(MidpointSnapPoint));
m_canvas.AddQuickSnapType(Keys.I, typeof(IntersectSnapPoint));
m_canvas.AddQuickSnapType(Keys.V, typeof(VertextSnapPoint));
m_canvas.AddQuickSnapType(Keys.P, typeof(PerpendicularSnapPoint));
m_canvas.AddQuickSnapType(Keys.Q, typeof(QuadrantSnapPoint));
m_canvas.AddQuickSnapType(Keys.C, typeof(CenterSnapPoint));
m_canvas.AddQuickSnapType(Keys.T, typeof(TangentSnapPoint));
m_canvas.AddQuickSnapType(Keys.D, typeof(DivisionSnapPoint)); m_canvas.KeyDown += new KeyEventHandler(OnCanvasKeyDown);
SetupMenuItems();
SetupDrawTools();
SetupLayerToolstrip();
SetupEditTools();
UpdateLayerUI(); MenuStrip menuitem = new MenuStrip();//创建文档的主菜单
menuitem.Items.Add(m_menuItems.GetMenuStrip("edit"));
menuitem.Items.Add(m_menuItems.GetMenuStrip("draw"));
menuitem.Visible = false;
Controls.Add(menuitem);
this.MainMenuStrip = menuitem;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
m_canvas.SetCenter(m_data.CenterPoint);
}
2.1查看SetCenter(UnitPoint unitPoint)方法,首先调用PointF point = ToScreen(unitPoint);将unitPoint转换PointF point.找到地图原点对应的屏幕坐标,应该是屏幕左下角点偏移(25,-25)。
/// <summary>
/// 设置画布到屏幕的中心
/// </summary>
/// <param name="rPoint">直角坐标系坐标</param>
public void SetCenter(RPoint unitPoint)
{
//将unitPoint点对应到屏幕上point
PointF point = Transform.ToScreen(unitPoint, this);
m_lastCenterPoint = unitPoint;
//将unitPoint偏移到屏幕中心
SetCenterScreen(point, false);
}
SetCenter
这里注意计算Unit坐标到屏幕坐标的ToScreen()方法中,transformedPoint.Y = ScreenHeight() - transformedPoint.Y;//将Unit坐标系转换为屏幕坐标系,Y轴反向
其中ScreenHeight()方法似乎有点问题,修改后如下。
/// <summary>
/// 将Unit坐标转换到屏幕坐标
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public PointF ToScreen(UnitPoint point)
{
PointF transformedPoint = Translate(point);
transformedPoint.Y = ScreenHeight() - transformedPoint.Y;//将Unit坐标系转换为屏幕坐标系,Y轴反向
transformedPoint.Y *= m_screenResolution * m_model.Zoom;
transformedPoint.X *= m_screenResolution * m_model.Zoom; transformedPoint.X += m_panOffset.X + m_dragOffset.X;
transformedPoint.Y += m_panOffset.Y + m_dragOffset.Y;
return transformedPoint;
}
ToScreen
float ScreenHeight()
{
return (float)(ToUnit(this.ClientRectangle.Height));
//return (float)(ToUnit(this.ClientRectangle.Height) / m_model.Zoom);
}
ScreenHeight
这样就引出了ToUnit(float screenvalue)函数,将屏幕距离转换为英寸数。
public double ToUnit(float screenvalue)
{
return (double)screenvalue / (double)(m_screenResolution * m_model.Zoom);
}
3. 接着将unitPoint赋值给m_lastCenterPoint
m_lastCenterPoint = unitPoint;
SetCenterScreen(point, false);
调用了SetCenterScreen()方法,
protected void SetCenterScreen(PointF screenPoint, bool setCursor)
{
float centerX = ClientRectangle.Width / ;
m_panOffset.X += centerX - screenPoint.X; float centerY = ClientRectangle.Height / ;
m_panOffset.Y += centerY - screenPoint.Y; if (setCursor)
Cursor.Position = this.PointToScreen(new Point((int)centerX, (int)centerY));
DoInvalidate(true);
}
4.理解了public PointF ToScreen(UnitPoint point),那public UnitPoint ToUnit(PointF screenpoint)也好理解了。
/// <summary>
/// 将屏幕坐标转换到Unit坐标
/// </summary>
/// <param name="screenpoint"></param>
/// <returns></returns>
public UnitPoint ToUnit(PointF screenpoint)
{
float panoffsetX = m_panOffset.X + m_dragOffset.X;
float panoffsetY = m_panOffset.Y + m_dragOffset.Y;
float xpos = (screenpoint.X - panoffsetX) / (m_screenResolution * m_model.Zoom);
float ypos = ScreenHeight() - ((screenpoint.Y - panoffsetY)) / (m_screenResolution * m_model.Zoom);
return new UnitPoint(xpos, ypos);
}
ToUnit
5.最后单独说一下ToScreen(UnitPoint point)和ToUnit(PointF screenpoint)中的两个变量
PointF m_panOffset = new PointF(, -);
PointF m_dragOffset = new PointF(, );
这里m_panOffset控制的是中心点Center的偏移量,是一个累计的量,相对于中心点。
m_dragOffset记录了每次移动过程中的移动量,每次产生一个新值。每次CanvasCtrl控件的OnMouseDown时累积到偏移量上,之后重新初始化,同时在OnMouseUp时的移动命令下重新初始化(如下代码),似乎重复了。
if (m_commandType == eCommandType.pan)
{
m_panOffset.X += m_dragOffset.X;
m_panOffset.Y += m_dragOffset.Y;
m_dragOffset = new PointF(, );
}
在protected override void OnMouseMove(MouseEventArgs e)事件中
if (m_commandType == eCommandType.pan && e.Button == MouseButtons.Left)
{
m_dragOffset.X = -(m_mousedownPoint.X - e.X);
m_dragOffset.Y = -(m_mousedownPoint.Y - e.Y);
m_lastCenterPoint = CenterPointUnit();
DoInvalidate(true);
}
可知,m_dragOffset和m_panOffset记录的是偏移的屏幕坐标。
[SharpMap] 屏幕坐标和Map坐标转换的更多相关文章
- PIE SDK屏幕坐标和地图坐标转换
1. 功能简介 屏幕坐标和地图坐标转换,就是字面意思,将电脑屏幕的坐标转换为带有空间信息的地图坐标,主要运用PIE SDK地图控件的ToMapPoint()方法,而地图坐标转换为设备坐标(屏幕),用的 ...
- 分享:Unity3D模型跟随鼠标移动功能的小脚本 (屏幕坐标和三维空间坐标转换)
using UnityEngine; using System.Collections; public class ModelsPosChange : MonoBehaviour { RaycastH ...
- C#+SharpMap的相关代码
//放大的代码: private void MapZoomIn(NameValueCollection queryString) { SharpMap.Map map = Session[" ...
- 一步一步手写GIS开源项目-(1)500行代码实现基础GIS展示功能
1.开篇 大学毕业工作已经两年了,上学那会就很想研读一份开源GIS的源码,苦于自己知识和理解有限,而市面上也没有什么由浅入深讲解开源gis原理的书籍,大多都是开源项目简介以及项目的简单应用.对于初级程 ...
- (十四)WebGIS中地图放大缩小的设计和实现
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 在上一章中,我们给出了整个工具栏设计的核心,使用命令模式,并 ...
- Unity经典游戏教程之:弓之骑士
版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...
- svg 实践之屏幕坐标与svg元素坐标转换
近期在做svg相关项目,很好用的东西要记下来: 1.基础知识就是根据 矩阵进行坐标转换,如下: : 屏幕坐标 = 矩阵* svg对象坐标 2.javascript有个方法用于获取 svg对象 的转换矩 ...
- 百度Map与HT for Web结合的GIS网络拓扑应用
在<HT for Web整合OpenLayers实现GIS地图应用>篇中介绍了HT for Web与OpenLayers的整合,不少朋友反应国内用得比较多的还是百度地图,虽然HT整合百度地 ...
- C# SharpMap的简单使用
本文是利用ShapMap实现GIS的简单应用的小例子,以供学习分享使用.关于SharpMap的说明,网上大多是以ShapeFile为例进行简单的说明,就连官网上的例子也不多.本文是自己参考了源代码进行 ...
随机推荐
- php通过字符串生存hashCode
/** * * 生存hashCode * */function hashCode($str){ if(empty($str)) return ''; $str = strtoupper($str); ...
- SVN入门 TortoiseSVN 检出
1. SVN检出(SVN Checkout) 检出项目文件. 新建或者进入目录下(比如qianduan1),右键 --> Svn 检出-->其中版本库URL我可以在SVN服务器获取到,将复 ...
- codeforces 547A Mike and Frog
近期都是这样的题呢. . .... 哎 開始想纯暴力(体如今跳出循环t>=那里.,,,)..,.随着数据变大.. ...(t=499981500166是能够的),,,..,,23333333 超 ...
- 零基础的人怎么学习Java
编程语言Java,已经21岁了.从1995年诞生以来,就一直活跃于企业中,名企应用天猫,百度,知乎......都是Java语言编写,就连现在使用广泛的XMind也是Java编写的.Java应用的广泛已 ...
- swift学习笔记之控制流
控制流: 1.if语句 let count = { print("yes") }else{ print("no") } 2.switch语句 (1)Swift中 ...
- sqlmap tutorial
svn checkout https://svn.sqlmap.org/sqlmap/trunk/sqlmap sqlmap-dev sqlmap.py -u "http://www.isl ...
- Apktool源码解析——第一篇
著名的apktool是android逆向界用的最普遍的一个工具,这个项目的原始地址在这里http://code.google.com/p/android-apktool/,但是你们都懂的在天朝谷歌是无 ...
- poj_3579 二分法
题目大意 给定N个数,这些数字两两求差构成C(N,2)(即N*(N-1)/2)个数值,求这C(N,2)个数的中位数.N <= 100000. 题目分析 根据数据规模N最大为100000,可知不能 ...
- 【Mysql】 case ... when ... 用法
sql语句查询时给某个空字段赋值 SELECT CASE WHEN field= '' THEN ' WHEN fieldIS NULL THEN ' ELSE field END FROM tabl ...
- 【Node.js】Mac 下安装node图文详解
1 进入官网,下载node最新版 官网:https://nodejs.org/en/ 2 双击下载的安装包,一路默认安装就行 3 打开终端,输入以下命令查看结果,如出现下图信息则为安装成功 4 ...