由于要求上采用Html来实现配准的画图功能(如果直接采用超图的接口、ArcGIS js API等会简单很多),所以有如下思路:地图采用了超图的接口,获取其已经发布的服务,地图显示在一个Div中,在地图上方叠加一个Canvas进行画图。所画图形经过平移旋转(缩放会有问题)后,配准地图上的要素,然后将屏幕坐标转换为地图坐标,得到配准结果。(此配准不是完全的影像配准,其过程有要求:所画图形不能发生形变)。
JS 和html 没有做分离,所有代码如下,有时间再回来编辑:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>第5个测试页面</title>
<style type="text/css">
*
{
margin: 0 0 0 0;
padding: 0 0 0 0;
}
body
{
overflow:auto;
background: #fff;
}
#map, #canvasOne
{
position: absolute;
width: 1000px;
height: 1000px;
top: 20px;
left: 30px;
}
#rightdiv
{
position:absolute;
top:20px;
left:1060px;
width: 380px;
height: 1000px;
display: inline-block;
overflow: hidden;
}
#FirstFloorDiv, #SecondFloorDiv
{
display: inline-block;
}
</style>
<script type="text/javascript" src="libs/SuperMap.Include.js"></script>
<script type="text/javascript">
///Canvas的所有画图功能
var JsonString = false; //记录Json的字符串(原始)
var Coordinates = false; //记录Json解析出来的坐标串(原始)
var Rectangle = false; //记录外包矩形框(原始)[minX, minY, maxX, maxY]
var Circle = false; //记录外包圆形(原始)[Center,Radius];Center:X,Y;
var canvas = false;
var Before_Coordinates = false;
var After_Coordinates = false;
var Point_FirstSelect = false;
var Point_FirstSelect_index = false;
var Point_SecondSelect = false;
var Point_SecondSelect_index = false;
var SwitchCase = 1; //默认为平移
var isOpenPan = 1; //是否为平移
var isOpenRotate = 2; //是否为旋转
var thelta = false; //旋转角
var banjing = 80; //缩放比例半径
var MapCenter = { X: 550, Y: 350 };//缩放平移的地图中心位置
//解析Json格式,返回Building坐标数组
function AnalyzeJson2Cos(JsonString) {
var obj = eval('(' + JsonString + ')');
return obj.Building.Coordinates;
}
//计算坐标串的外包矩形
function CompRectangle(Cos) {
var minX = Cos[0].X;
var minY = Cos[0].Y;
var maxX = Cos[0].X;
var maxY = Cos[0].Y;
for (var i = 1; i < Cos.length; i++) {
if (Cos[i].X > maxX) maxX = Cos[i].X;
if (Cos[i].X < minX) minX = Cos[i].X;
if (Cos[i].Y > maxY) maxY = Cos[i].Y;
if (Cos[i].Y < minY) minY = Cos[i].Y;
}
Rectangle = { minX: minX, minY: minY, maxX: maxX, maxY: maxY };
return Rectangle;
}
//计算坐标串的外包圆形
function CompCircle(Rectangle) {
var Center = { X: (Rectangle.maxX - Rectangle.minX) + Rectangle.minX, Y: (Rectangle.maxY - Rectangle.minY) + Rectangle.minY };
var Radius = 0.5 * Math.sqrt((Rectangle.maxX - Rectangle.minX) * (Rectangle.maxX - Rectangle.minX) + (Rectangle.maxY - Rectangle.minY) * (Rectangle.maxY - Rectangle.minY));
Circle = { Center: Center, Radius: Radius };
return Circle;
}
//计算两点距离
function CompDistance(P1, P2) {
return Math.sqrt((P1.X - P2.X) * (P1.X - P2.X) + (P1.Y - P2.Y) * (P1.Y - P2.Y));
}
//计算两点构成的直线与正北方向的夹角
function ComputeAngle(FPoint, LPoint) {
var dRotateAngle;
if (FPoint.Y === LPoint.Y) {
if (FPoint.X < LPoint.X) { dRotateAngle = Math.PI / 2; }
else { dRotateAngle = Math.PI / 2 * 3; }
return dRotateAngle;
}
else {
var deltX = Math.abs(FPoint.X - LPoint.X);
var deltY = Math.abs(FPoint.Y - LPoint.Y);
dRotateAngle = Math.atan(deltX / deltY);
}
var PI = Math.PI;
//如果下一点的横坐标大于前一点(在第一和第四象限)
if (LPoint.X >= FPoint.X) {
//在第一象限(0<=dRotateAngle<=90)
if (LPoint.Y <= FPoint.Y) {
dRotateAngle = dRotateAngle;
}
else {
dRotateAngle = PI - dRotateAngle;
}
}
else//(在第二和第三象限)
{
if (LPoint.Y <= FPoint.Y) {
dRotateAngle = 2 * PI - dRotateAngle;
}
else {
dRotateAngle = PI + dRotateAngle;
}
}
return dRotateAngle;
}
//一个简单的莫卡托转经纬度
function Mercator2lonLat(mercator) {
var lonLat = { X: 0, Y: 0 };
var x = mercator.X / 20037508.34 * 180;
var y = mercator.Y / 20037508.34 * 180;
y = 180 / Math.PI * (2 * Math.atan(Math.exp(y * Math.PI / 180)) - Math.PI / 2);
lonLat.X = x;
lonLat.Y = y;
alert(lonLat.X);
return lonLat;
}
//将所有坐标缩放到地图中心位置(目前是通过外包圆中心进行缩放,这样会导致某些问题,下一步考虑是否采用坐标原点进行缩放)
function ZoomSystem(objCoords) {
var objCircle = CompCircle(CompRectangle(objCoords));
//进行缩放,对所有坐标进行缩放 要分四个象限去算
var scale = banjing / objCircle.Radius;
if (scale > 1.5 || scale < 0.5) {
for (var i = 0; i < Coordinates.length; i++) {
var length = CompDistance(objCircle.Center, objCoords[i]);
var NorthAngle = ComputeAngle(objCircle.Center, objCoords[i]);
if (objCoords[i].X > objCircle.Center.X) {
objCoords[i].X -= Math.abs(length * (1 - scale) * Math.sin(NorthAngle));
if (objCoords[i].Y > objCircle.Center.Y) {
objCoords[i].Y -= Math.abs(length * (1 - scale) * Math.cos(NorthAngle));
}
else {
objCoords[i].Y += Math.abs(length * (1 - scale) * Math.cos(NorthAngle));
}
}
else {
objCoords[i].X += Math.abs(length * (1 - scale) * Math.sin(NorthAngle));
if (objCoords[i].Y > objCircle.Center.Y) {
objCoords[i].Y -= Math.abs(length * (1 - scale) * Math.cos(NorthAngle));
}
else {
objCoords[i].Y += Math.abs(length * (1 - scale) * Math.cos(NorthAngle));
}
}
}
objCircle = CompCircle(CompRectangle(objCoords));
}
//平移到MapCenter
var deltX = objCircle.Center.X - MapCenter.X;
var deltY = objCircle.Center.Y - MapCenter.Y;
for (var i = 0; i < Coordinates.length; i++) {
objCoords[i].X -= deltX;
objCoords[i].Y -= deltY;
}
return objCoords;
}
//在Canvas界面上画出 原始 图形
function drawJson() {
//获取Json序列中的坐标串
JsonString = document.getElementById('Jsontxt').value;
Coordinates = AnalyzeJson2Cos(JsonString);
//获取待操作的坐标数组
Before_Coordinates = copyObject(ZoomSystem(Coordinates));
After_Coordinates = copyObject(ZoomSystem(Coordinates));
//初始化canvas进行画图
var canvas = document.getElementById("canvasOne");
//简单地检测当前浏览器是否支持Canvas对象,以免在一些不支持html5的浏览器中提示语法错误
if (canvas.getContext) {
//获取对应的CanvasRenderingContext2D对象(画笔)
var ctx = canvas.getContext("2d");
//开始一个新的绘制路径
ctx.beginPath();
ctx.strokeStyle = "blue";
//设置路径起点坐标
ctx.moveTo(Coordinates[0].X, Coordinates[0].Y);
for (var i = 1; i < Coordinates.length; i++) {
ctx.lineTo(Coordinates[i].X, Coordinates[i].Y);
}
//先关闭绘制路径。注意,此时将会使用直线连接当前端点和起始端点。
ctx.closePath();
ctx.lineWidth = 1;
//最后,按照绘制路径画出直线
ctx.stroke();
}
//初始化。
windowLoadHandler();
}
var Debugger = function () { };
Debugger.log = function (message) {
try {
console.log(message);
}
catch (exception) {
return;
}
}
//初始化入口
function windowLoadHandler() {
canvasApp();
}
//判断浏览器对canvas是否支持
function canvasSupport() {
//return Modernizr.canvas;
return true;
}
function canvasApp() {
if (!canvasSupport()) {
return;
}
canvas = document.getElementById("canvasOne");
var context = canvas.getContext("2d");
init();
var shapes;
var dragIndex;
var dragging;
var mouseX;
var mouseY;
var dragHoldX;
var dragHoldY;
function init() {
shapes = Coordinates;
drawScreen(Coordinates);
canvas.addEventListener("mousedown", mouseDownListener, false);
}
function mouseDownListener(evt) {
//如果是右键点击,直接进入旋转步骤
if (evt.button == 2) { ChangeSwitcher(isOpenRotate); }
//else if (evt.button == 1) { ChangeSwitcher(isOpenPan); }
var highestIndex = -1;
var bRect = canvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left) * (canvas.width / bRect.width);
mouseY = (evt.clientY - bRect.top) * (canvas.height / bRect.height);
//找到哪个是被选中的点
switch (SwitchCase) {
case isOpenPan:
//find which shape was clicked
for (var i = 0; i < Coordinates.length; i++) {
if (hitTest(Before_Coordinates[i], mouseX, mouseY)) {
dragging = true;
if (i > highestIndex) {
//We will pay attention to the point on the object where the mouse is "holding" the object:
dragHoldX = mouseX - Before_Coordinates[i].X;
dragHoldY = mouseY - Before_Coordinates[i].Y;
highestIndex = i;
dragIndex = i;
Point_FirstSelect = copyObject(Before_Coordinates[dragIndex]);
Point_FirstSelect_index = dragIndex;
}
}
}
break;
case isOpenRotate:
//find which shape was clicked
for (var i = 0; i < Coordinates.length; i++) {
if (hitTest(Before_Coordinates[i], mouseX, mouseY)) {
dragging = true;
if (i == Point_FirstSelect_index) {
alert("控制点选择出现重复!");
return;
}
if (i > highestIndex) {
//We will pay attention to the point on the object where the mouse is "holding" the object:
dragHoldX = mouseX - Before_Coordinates[i].X;
dragHoldY = mouseY - Before_Coordinates[i].Y;
highestIndex = i;
dragIndex = i;
Point_FirstSelect = copyObject(Before_Coordinates[Point_FirstSelect_index]);
Point_SecondSelect = copyObject(Before_Coordinates[dragIndex]);
Point_SecondSelect_index = dragIndex;
}
}
}
break;
default: break;
}
if (dragging) { window.addEventListener("mousemove", mouseMoveListener, false);}
canvas.removeEventListener("mousedown", mouseDownListener, false);
window.addEventListener("mouseup", mouseUpListener, false);
//code below prevents the mouse down from having an effect on the main browser window:
if (evt.preventDefault) {
evt.preventDefault();
} //standard
else if (evt.returnValue) {
evt.returnValue = false;
} //older IE
return false;
}
var isMouseUp = false;
function mouseUpListener(evt) {
if (!isMouseUp) {
canvas.addEventListener("mousedown", mouseDownListener, false);
window.removeEventListener("mouseup", mouseUpListener, false);
if (dragging) {
dragging = true;
}
isMouseUp = true;
}
else {
canvas.addEventListener("mousedown", mouseDownListener, false);
window.removeEventListener("mouseup", mouseUpListener, false);
if (dragging) {
dragging = false;
window.removeEventListener("mousemove", mouseMoveListener, false);
Before_Coordinates = copyObject(After_Coordinates);
}
isMouseUp = false;
}
}
function mouseMoveListener(evt) {
var posX;
var posY;
var shapeRad = shapes[dragIndex].rad;
var minX = shapeRad;
var maxX = canvas.width - shapeRad;
var minY = shapeRad;
var maxY = canvas.height - shapeRad;
//getting mouse position correctly
var bRect = canvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left) * (canvas.width / bRect.width);
mouseY = (evt.clientY - bRect.top) * (canvas.height / bRect.height);
//clamp x and y positions to prevent object from dragging outside of canvas,dragHold是缓冲半径
posX = mouseX - dragHoldX;
posX = (posX < minX) ? minX : ((posX > maxX) ? maxX : posX);
posY = mouseY - dragHoldY;
posY = (posY < minY) ? minY : ((posY > maxY) ? maxY : posY);
//这里要重新计算坐标,可以根据这个值来计算坐标偏移量
After_Coordinates[dragIndex].X = posX;
After_Coordinates[dragIndex].Y = posY;
var MousePoint = { X: posX, Y: posY };
switch (SwitchCase)
{
case isOpenPan:
//所有坐标的平移差量
var deltaX = After_Coordinates[dragIndex].X - Before_Coordinates[dragIndex].X;
var deltaY = After_Coordinates[dragIndex].Y - Before_Coordinates[dragIndex].Y;
showCos(deltaX, deltaY, MousePoint);
moveOriginPic2Current(deltaX, deltaY);
drawScreen(After_Coordinates);
break;
case isOpenRotate:
//计算夹角
var jd1 = ComputeAngle(Point_FirstSelect, Point_SecondSelect);
var jd2 = ComputeAngle(Point_FirstSelect, MousePoint);
var jd_abs = Math.abs(jd1 - jd2);
//顺时针转逆时针
if (jd1 < jd2) thelta = (2 * Math.PI - jd_abs);
else thelta = jd_abs;
showCos2(thelta,MousePoint);
rotateCoords(thelta, Point_FirstSelect, MousePoint);
drawScreen(After_Coordinates);
break;
default:
break; ;
}
}
//对所有坐标进行平移
function moveOriginPic2Current(deltX, deltY) {
for (var i = 0; i < Coordinates.length; i++) {
After_Coordinates[i].X = Before_Coordinates[i].X + deltX;
After_Coordinates[i].Y = Before_Coordinates[i].Y + deltY;
}
}
//对所有坐标进行旋转变换,FPoint为所选择的第一个点,angle记录为与逆时针旋转角度,mousePoint是当前鼠标点
function rotateCoords(angle, FPoint, mousePoint) {
for (var i = 0; i < Coordinates.length; i++) {
if (i != Point_FirstSelect_index) {
//canvas的坐标轴是发生转变的
var x0 = (Before_Coordinates[i].X - FPoint.X) * Math.cos(angle) - (FPoint.Y - Before_Coordinates[i].Y) * Math.sin(angle) + FPoint.X;
var y0 = (Before_Coordinates[i].X - FPoint.X) * Math.sin(angle) + (FPoint.Y - Before_Coordinates[i].Y) * Math.cos(angle) - FPoint.Y;
After_Coordinates[i].X = x0;
After_Coordinates[i].Y = -y0; //y=-y
}
}
}
//判断点击的位置是否为拾取点
function hitTest(shape, mx, my) {
var dx = mx - shape.X;
var dy = my - shape.Y;
var d = document.getElementById('BufferRange').value;
return ((dx * dx + dy * dy) < (d * d)); //所选的缓冲半径为4
}
//根据坐标数组在canvas上画图(全部为直线)
function drawShapes(Coords) {
context.beginPath();
context.strokeStyle = "blue";
//设置路径起点坐标
context.moveTo(Coords[0].X, Coords[0].Y);
for (var i = 1; i < Coordinates.length; i++) {
context.lineTo(Coords[i].X, Coords[i].Y);
}
//先关闭绘制路径。注意,此时将会使用直线连接当前端点和起始端点。
context.lineWidth = 2;
//最后,按照绘制路径画出直线
context.closePath();
context.stroke();
}
//画图的屏幕处理
function drawScreen(coords) {
context.clearRect(0, 0, canvas.width, canvas.height);
drawShapes(coords);
}
}
//显示备准的结果
function showResult(cos) {
var s = "";
s += "<From x,y> <space> <To x,y> \r\n"
for (var i = 0; i < Coordinates.length; i++) {
//cos[i] = Mercator2lonLat(cos[i]);
s += Coordinates[i].X + "," + Coordinates[i].Y + " " + cos[i].lon + "," + cos[i].lat + "\r\n";
}
document.getElementById('Result').value = s;
}
///地图操作
var map, layer;
//var url = "http://101.251.247.242:8090/iserver/services/map-jingjin/rest/maps/京津地区地图";
//var Center_LonLat =new SuperMap.LonLat(117.01, 40.04);
var url = "http://101.251.247.242:8090/iserver/services/map-changchun/rest/maps/长春市区图";
var Center_LonLat =new SuperMap.LonLat(5105, -3375);
var vectorLayer;
var FeatureStyle = {
strokeColor: "#304DBE",
strokeWidth: 2,
pointerEvents: "visiblePainted",
fillColor: "#304DBE",
fillOpacity: 0.8
};
function init() {
map = new SuperMap.Map("map", { controls: [
new SuperMap.Control.Navigation(),
new SuperMap.Control.Zoom(),
new SuperMap.Control.MousePosition()]
});
layer = new SuperMap.Layer.TiledDynamicRESTLayer("China", url, null, { maxResolution: "auto" });
layer.events.on({ "layerInitialized": addLayer });
//添加图层
vectorLayer = new SuperMap.Layer.Vector("Vector Layer", { renderers: ["Canvas2"] });
map.addLayer(vectorLayer);
//地图上移动鼠标时显示坐标
map.events.on({
"mousemove": function (e) {
var Position = e.xy.clone();
var pixcel = new SuperMap.Pixel(e.layerX, e.layerY);
var currPoint = map.getLonLatFromPixel(pixcel);
document.getElementById('Coords_pic').value = currPoint.lon + "," + currPoint.lat;
}
});
}
//初始化完成后添加图层
function addLayer() {
map.addLayer(layer);
map.setCenter(Center_LonLat, 0);
}
//调整canvas、pic的层次关系,控制canvas在上面一层
function ShowCanvas() {
document.getElementById('canvasOne').style.zIndex = 2;
document.getElementById('map').style.zIndex = 1;
}
//调整canvas、pic的层次关系,控制地图在上面一层
function ShowMap() {
document.getElementById('canvasOne').style.zIndex = 1;
document.getElementById('map').style.zIndex = 2;
}
//执行按钮,将要素绘制到地图的矢量图层上
function Work() {
//将pixel点数组 转为 地理坐标组
var SuperCoordnates = getSuperMapCoords();
var SuperGeometries = getSuperMapGeometries(SuperCoordnates);
var linearRings = new SuperMap.Geometry.LinearRing(SuperGeometries);
var Polygon = new SuperMap.Geometry.Polygon([linearRings]);
var PolygonFeature = new SuperMap.Feature.Vector(Polygon);
PolygonFeature.style = FeatureStyle;
//添加要素
vectorLayer.addFeatures([PolygonFeature]);
showResult(SuperCoordnates);
}
//由超图经纬度坐标串生成超图点对象数组
function getSuperMapGeometries(s) {
var points = [];
for (var i = 0; i < Coordinates.length; i++) {
points.push(new SuperMap.Geometry.Point(s[i].lon, s[i].lat));
}
return points;
}
//由屏幕坐标串转为超图经纬度坐标串
function getSuperMapCoords() {
var s = [];
for (var i = 0; i < Coordinates.length; i++) {
var lonlat = map.getLonLatFromPixel(new SuperMap.Pixel(After_Coordinates[i].X, After_Coordinates[i].Y));
s.push(lonlat);
}
return s;
}
//改变操作状态
function ChangeSwitcher(SWCase) {
switch (SWCase) {
case 1:
SwitchCase = 1;
break;
case 2:
SwitchCase = 2;
break;
default:
break;
}
Before_Coordinates = copyObject(After_Coordinates);
}
//对象深拷贝的一种方法
function copyObject(source) {
var result = {};
for (var key in source) {
result[key] = typeof source[key] === 'object' ? copyObject(source[key]) : source[key];
}
return result;
}
//这一块都用于测试
//在平移的时候展示中间参数
function showCos(dx,dy,MouseP) {
var s = "";
s += "鼠标位置(pixel):\r\nX:" + MouseP.X + " Y:" + MouseP.Y + "\r\n";
s += "拖拽点编号:" + Point_FirstSelect_index + "\r\n";
s += "旋转中心点\r\n:X-" + Point_FirstSelect.X + "Y-" + Point_FirstSelect.Y + "\r\n";
s += "平移量: X-"+dx +" Y-"+dy+ "\r\n";
document.getElementById("showCoords").value = s;
}
//在旋转的时候展示中间参数
function showCos2(angle,MouseP) {
var s = "";
s += "逆时针旋转角:" + 180/Math.PI*angle + "度\r\n";
s += "鼠标位置(pixel)X:" + MouseP.X + " " + "鼠标Y:" + MouseP.Y+"\r\n";
s += "旋转中心点坐标:\r\nX-" + Point_FirstSelect.X + " Y-" + Point_FirstSelect.Y + "\r\n";
s += "定位方向点坐标:\r\nX-" + Point_SecondSelect.X + " Y-" + Point_SecondSelect.Y + "\r\n";
document.getElementById("showCoords").value = s;
}
//测试用的方法
function getSomeTestInfo() {
alert("Canvas的index:" + document.getElementById('Pic').style.zIndex);
alert("map的index:" + document.getElementById('map').style.zIndex);
displayProp("Pic的Style属性:" + document.getElementById('Pic').style);
}
//获取js对象所有属性(这个方法用于测试)
function displayProp(obj) {
var names = "";
for (var name in obj) {
names += name + ": " + obj[name] + ", \r\n";
}
alert(names);
}
//使用滑轮直接进入地图
function Wheel2Map(e) {
document.getElementById('canvasOne').style.zIndex = 1;
document.getElementById('map').style.zIndex = 2;
}
//判断缓冲范围必须为正数
function valueIsPositive() {
if (document.getElementById('BufferRange').value <= 0) {
alert("缓冲范围请填写正值");
document.getElementById('BufferRange').value = 10;
}
}
function Reset() {
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, 1000, 1000);
vectorLayer.removeAllFeatures();
drawJson();
SwitchCase = isOpenPan;
}
</script>
</head>
<body οnlοad="init()" onmousewheel="Wheel2Map(event)";>
<div id="map"></div>
<canvas id="canvasOne" width="1000px" height="1000px"></canvas>
<div id="rightdiv">
<div id="FirstFloorDiv">
<input type="button" value="显示建筑" οnclick="ShowCanvas()" />
<input type="button" value="显示地图" οnclick="ShowMap()" />
 
<input type="button" value="进行平移" οnclick="ChangeSwitcher(1)" />
<input type="button" value="进行旋转" οnclick="ChangeSwitcher(2)" />
 
<input type="button" value="重启" οnclick="Reset()" />
</br></br>地图数据源:
<select id="DataSource" name="DataSource" autofocus="true">
<option value="超图长春">超图长春</option>
<option value="超图数据服务">超图数据服务</option>
<option value="WMS服务">WMS服务</option>
</select>
</div>
<div id="SecondFloorDiv">
<div id="top_CalibrationDiv">
请在下方输入框中粘贴Json格式字符串:</br>
<textarea id="Jsontxt"></textarea>
<input id="Calibration" type="button" style="left:120px" value="配准" οnclick="drawJson()" />
<input id="Go" type="button" value="执行" οnclick="Work()" />
</div>
<div id="PicBox">
点拾取的缓冲范围(pixel):<input type="text" id="BufferRange" style="width: 30px" value="10" οnblur="valueIsPositive()"/></br>
地图坐标:<input type="text" id="Coords_pic" style="width: 98%" />
</br>当前参数:</br>
<textarea id="showCoords" style="height: 320px; width:98%"></textarea>
</br>输出结果:</br>
<textarea id="Result" style="height: 450px; width:98%"></textarea>
</div>
</div>
</div>
</body>
</html>
<script type="text/javascript">
//一些琐碎的处理
//禁用Canvas的右键菜单
document.getElementById('canvasOne').oncontextmenu = function () {
return false;
}
</script>