[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为例进行简单的说明,就连官网上的例子也不多.本文是自己参考了源代码进行 ...
随机推荐
- ubuntu-12.04.5-desktop-amd64.iso:ubuntu-12.04.5-desktop-amd64:安装Oracle11gR2
ubuntu 桌面版的安装不介绍. 如何安装oracle:核心步骤和关键点. ln -sf /bin/bash /bin/sh ln -sf /usr/bin/basename /bin/basena ...
- KAFKA安装+配置详解+常用操作+监控
http://blog.csdn.net/hadas_wang/article/details/50056381 http://qiyishi.blog.51cto.com/5731577/18575 ...
- Kafka中Producer端封装自定义消息
我们知道KeywordMessage就是被kafka发送和存储的对象.所以只需要模拟出这个就可以发送自定义消息了. 比如我需要将用户的id,user,age,address和访问ip和访问date记录 ...
- ssh通过密钥进行连接
sshd服务提供两种安全验证的方法: 基于口令的安全验证:经过验证帐号与密码即可登陆到远程主机. 基于密钥的安全验证:需要在本地生成"密钥对"后将公钥传送至服务端,进行公共密钥的比 ...
- 计算从ios照片库中选取的图片文件大小
本文转载至:http://blog.csdn.net/longzs/article/details/8373586 从 iphone 的 照片库中选取的图片,由于 系统不能返回其文件的具体路径,所以这 ...
- LeetCode——Best Time to Buy and Sell Stock II
Description: Say you have an array for which the ith element is the price of a given stock on day i. ...
- DOM操作的性能问题
造成DOM操作性能差的原因:1.DOM操作的实现和ECMAscript的实现是两个独立的部分,之间通过接口来完成相应的DOM操作. 2.实时查询文档得到HTML集合,重复执行查询操作.特别是lengt ...
- Excel 2010 如何在Excel的单元格中加入下拉选项
http://jingyan.baidu.com/article/03b2f78c4ba8a05ea237ae95.html 第一步:打开excel文档,选中需加入下拉选项的单元格. 第二步:点击 ...
- Excel 2010 如何将筛选后的数据复制粘贴到另一个工作表筛选后的表格里
如果你是指自动筛选后,把筛选数据复制/粘贴到另外一个工作表中,不妨试试试 第一步选中筛选后的数据区域:第二步执行菜单命令“编辑/定位/定位条件/可见单元格”,确定:第三步单击复制按钮或者Ctrl+C或 ...
- 安装php环境xampp
1.下载xampp 安装 2.如果启动时发生端口占用错误, 是443和80端口被占用, 可以改成444,88端口, 在C:\xampp\apache\conf\extra\httpd-ssl.conf ...