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坐标转换的更多相关文章

  1. PIE SDK屏幕坐标和地图坐标转换

    1. 功能简介 屏幕坐标和地图坐标转换,就是字面意思,将电脑屏幕的坐标转换为带有空间信息的地图坐标,主要运用PIE SDK地图控件的ToMapPoint()方法,而地图坐标转换为设备坐标(屏幕),用的 ...

  2. 分享:Unity3D模型跟随鼠标移动功能的小脚本 (屏幕坐标和三维空间坐标转换)

    using UnityEngine; using System.Collections; public class ModelsPosChange : MonoBehaviour { RaycastH ...

  3. C#+SharpMap的相关代码

    //放大的代码: private void MapZoomIn(NameValueCollection queryString) { SharpMap.Map map = Session[" ...

  4. 一步一步手写GIS开源项目-(1)500行代码实现基础GIS展示功能

    1.开篇 大学毕业工作已经两年了,上学那会就很想研读一份开源GIS的源码,苦于自己知识和理解有限,而市面上也没有什么由浅入深讲解开源gis原理的书籍,大多都是开源项目简介以及项目的简单应用.对于初级程 ...

  5. (十四)WebGIS中地图放大缩小的设计和实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 在上一章中,我们给出了整个工具栏设计的核心,使用命令模式,并 ...

  6. Unity经典游戏教程之:弓之骑士

    版权声明: 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号"优梦创客&qu ...

  7. svg 实践之屏幕坐标与svg元素坐标转换

    近期在做svg相关项目,很好用的东西要记下来: 1.基础知识就是根据 矩阵进行坐标转换,如下: : 屏幕坐标 = 矩阵* svg对象坐标 2.javascript有个方法用于获取 svg对象 的转换矩 ...

  8. 百度Map与HT for Web结合的GIS网络拓扑应用

    在<HT for Web整合OpenLayers实现GIS地图应用>篇中介绍了HT for Web与OpenLayers的整合,不少朋友反应国内用得比较多的还是百度地图,虽然HT整合百度地 ...

  9. C# SharpMap的简单使用

    本文是利用ShapMap实现GIS的简单应用的小例子,以供学习分享使用.关于SharpMap的说明,网上大多是以ShapeFile为例进行简单的说明,就连官网上的例子也不多.本文是自己参考了源代码进行 ...

随机推荐

  1. 【Mongo】数据备份与还原

    http://blog.51yip.com/nosql/1573.html mongorestore -d 数据库名 -c 集合名 --drop **.bson

  2. Ubuntu 13.04 安装 Oracle11gR2

    #step 1: groupadd -g 2000 dbauseradd -g 2000 -m -s /bin/bash -u 2000 griduseradd -g 2000 -m -s /bin/ ...

  3. mysqldump进行数据库的全备时,备份数据库的顺序是什么,就是先备份哪个库,然后再备份哪个库

    需求描述: 今天在用mysqldump工具进行数据库的备份的时候,突然想了一个问题,比如我有10个库要进行备份 那么是先备份哪个,然后再备份哪个呢,所以,做了实验,验证下. 操作过程: 1.使用--a ...

  4. linux磁盘分区详解

    http://blog.csdn.net/aaronychen/article/details/2270048

  5. 编写高性能的jQuery代码

    jQuery Optimization 现在jQuery已经出现在很多项目中,然而许多同学忽略了他的性能问题以及代码质量问题, 下面是我对jQuery的一些性能方面的学习. 选择器 选择器是jQuer ...

  6. POJ 1946 Cow Cycling(抽象背包, 多阶段DP)

    Description The cow bicycling team consists of N (1 <= N <= 20) cyclists. They wish to determi ...

  7. WebService远程调用(代码调用)

    在做多个系统集成的时候,由于各系统厂商采用不同的架构,在项目实施前期,各业务对业务理解不够深入,系统接口可能会有较多变化, 在此背景下,动态调用webserivce就变得灵活了,降低了系统集成的耦合度 ...

  8. GIS-007-Terrain跨域访问

    方法一: 在数据服务目录中添加一个Web.config文件,文件内容是: <?xml version="1.0" encoding="UTF-8"?> ...

  9. Spring学习笔记--在SpEL中筛选集合

    要用到Spring的util(包括util:list等),xml文件中的beans中需要添加一些有关util的信息: <?xml version="1.0" encoding ...

  10. LLDB调试器

    你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值? NSLog(@"%@", whatIsInsideThisThing); 或者跳过一个函数调用来简化程序的行为? NSNu ...