[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为例进行简单的说明,就连官网上的例子也不多.本文是自己参考了源代码进行 ...
随机推荐
- Unity3D面试——真实的面试,unity3d面试
本来想写一个系列的,一半是抨击现在面试之水,要人之奸,用大哥的话说,要走新手是做螺丝钉和抹布用的.另一半是对出出学校的或者是自废武功转3d的朋友们提供一个比较有价值的参考.不过我时间实在仓促.没有保证 ...
- NodeJs生成SVG图形验证码
背景:短信接口有调用限制,如果受到恶意攻击,很容易就爆掉,所以需要一系列验证机制,后端采用签名加密的方式,而前端要做人机识别,有两个要求: 1)不能使用文本式的验证码,很简单就能拿到 2)所有验证逻辑 ...
- Redis生成Id主键的工具
public class PrimaryKeyGenerator { private static readonly NedisClient client = new NedisClient(GetR ...
- 深入volley(三)自己来写volley
https://github.com/Smalinuxer/android-SpillOver 这是我自己写的一个请求缓存框架,基于volley的,沿袭了volley的架构与设计思想,而对其进一步的封 ...
- 超全面的JavaWeb笔记day08<Tomcat&Web应用&HTTP协议>
1.常用软件体系结构 BS:浏览器/服务器 CS:客户端/服务器 WEB资源 动态资源 JSP Servlet 静态资源 html 常用服务器 Tomcat Weblogic Resin JBOSS ...
- swift - UIPickerView 的使用
效果显示数下图: 1.初始化 pickerView.center = self.view.center //将dataSource设置成自己 pickerView.dataSource=self // ...
- raw_input()
raw_input() 用于接收标准输入,并把标准输入当成字符串类型来处理,只能在 Python2 中使用,Python3 中没有这个函数 #!/usr/bin/env python #-*- cod ...
- android 仿QQ手机版
千人2群开启,欢迎大家围观打酱油,群号145667827 您当前位置 : JavaApk-安卓应用游戏源码服务专家 » QQ » Android项目源码界面超级华丽的仿QQ最新版本 Andro ...
- iOS 自动布局 Autolayout 优先级的使用
一.约束的优先级 0.屏幕适配 发展历程 代码计算frame -> autoreszing(父控件和子控件的关系) -> autolayout(任何控件都可以产生关系) -> siz ...
- 实现类似printf这样的函数
来源:http://www.vimer.cn/2009/12/cc%E5%AE%9E%E7%8E%B0%E5%A4%9A%E5%8F%82%E6%95%B0%E5%87%BD%E6%95%B0%E7% ...