OpenLayer 学习之自定义测量控件(目前ol3的版本不会抛异常)
一、自定义控件是在继承基类空间基础上实现的,控件不是我写的(毕竟技术有限)最近也在一直在研究源码进行模仿想写出自己的功能更为强大的控件。
二、控件源码
1、css样式设置
.tooltip {
position: relative;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
color: white;
padding: 4px 24px 4px 8px;
opacity: 0.7;
white-space: nowrap;
}
.tooltip-measure {
opacity: 1;
font-weight: bold;
}
.tooltip-static {
background-color: #ffcc33;
color: black;
border: 1px solid white;
}
.tooltip-measure:before,
.tooltip-static:before {
border-top: 6px solid rgba(0, 0, 0, 0.5);
border-right: 6px solid transparent;
border-left: 6px solid transparent;
content: "";
position: absolute;
bottom: -6px;
margin-left: -7px;
left: 50%;
}
.tooltip-static:before {
border-top-color: #ffcc33;
}
.ol-popup-closer {
text-decoration: none;
position: absolute;
top: 4px;
right: 8px;
color: red;
}
.ol-popup-closer:after {
content: "✖";
}
/*MeasureTool*/
.MeasureTool{
position: absolute;
top: 2.0em;
right: 5em;
text-align: left;
padding: 0;
}
.MeasureTool .ulbody{
display: none;
}
.MeasureTool.shown .ulbody{
display: block;
}
.ulbody li input:focus, .ulbody li input:hover {
background-color: white;
color: blue;
font-weight: bold;
}
.MeasureTool ul {
padding: 0;
list-style: none;
margin: 0;
}
.MeasureTool ul li{
text-align: center;
}
.MeasureTool>ul>li>input{
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABiUlEQVRIS8WUQU7CQBSG/ze6lxsoJxCPYMpePIG4almJW2giJq1xJ6ykbuAG6p6GIwgnkBsYtwb7zGsZLKUk0tI4m6btzP/N/+bNTyh4UMH6+D+A7fkdACwOHdO4TTq1vVENoGP9PW2O/NvowPb8UDwafO6Y1Zc4xO77ExDigFStvwEYPccymhrQGYxL86/gIw50TCM7gBkz1zLKWrD16NeVwgCMqXaRGcDgTwIdzANVvm+czgTS9kZDAl0AkLO5WZxTNgfM/EpEZwxcu6bRjQD+jIDDgNSJ4uAtFyAIcCnlEJBrVWutp3FFRMWZa1ZLuhkyl0hKs6+Cd73Ltuc3CXjQwNwA2ZkuibQrM+pSMnF21zCGOwHYfb8LwhUYPSauxw99N4Do1j7/3jtMHcuoyHsegESFxET4lN7Hnpoo/j6Kvkc3exEpy3nJSCk+7OKhtinYton0pB6thlooFZYkx1hZnwbIob2+NA2wlv1bEsNs0kMAK5Z012wpupye1Cu+i7Lu9K/rCnfwA90A1i8VnCB2AAAAAElFTkSuQmCC') /*logo.png*/;
background-position: center center;
background-repeat: no-repeat;
}
.MeasureTool input[type="button"]{
background-color: rgba(255, 255, 255, 0.4);
width: 60px;
height: 26px;
border: 0;
}
.MeasureTool .ulbody li{
border-top: 1px solid rgba(221, 221, 221, 0.4);
}
2、JS源码
/**
* OpenLayers 3 MeasureTool Control.
* See [the examples](./examples) for usage.
* @constructor
* @extends {ol.control.Control}
* @param {Object} opt_options Control options, extends olx.control.ControlOptions adding:
* **`tipLabel`** `String` - the button tooltip.
*/
//构造函数
ol.control.MeasureTool = function(opt_options) {
var options = opt_options || {};
this.sphereradius = options.sphereradius ?
options.sphereradius : 6378137;
this.mapListeners = [];
// hiddenclass
this.hiddenClassName = 'ol-control MeasureTool';
if (ol.control.MeasureTool.isTouchDevice_()) {
this.hiddenClassName += ' touch';
}
// shownClass
this.shownClassName = this.hiddenClassName + ' shown';
var element = document.createElement('div');
element.className = this.hiddenClassName;
this.panel = document.createElement('ul');
element.appendChild(this.panel);
var ulheader = document.createElement('li');
this.panel.appendChild(ulheader);
var inputMeasure = document.createElement('input');
inputMeasure.type = "button";
ulheader.appendChild(inputMeasure);
var ulbody = document.createElement('li');
this.panel.appendChild(ulbody);
var html = '';
html += '<ul class="ulbody">';
html += '<li><input type="button" value="Line"></li>';
html += '<li><input type="button" value="Area"></li>';
html += '<li><input type="checkbox" value="no"></li>';
html += '</ul>';
ulbody.innerHTML = html;
var this_ = this;
inputMeasure.onmouseover = function(e) {
this_.showPanel();
};
inputMeasure.onclick = function(e) {
e = e || window.event;
this_.showPanel();
e.preventDefault();
};
var lis = ulbody.getElementsByTagName("li");
this.sourceMesure = new ol.source.Vector();
this.vectorMesure = new ol.layer.Vector({
source: this.sourceMesure,
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new ol.style.Stroke({
color: '#ffcc33',
width: 2
}),
image: new ol.style.Circle({
radius: 7,
fill: new ol.style.Fill({
color: '#ffcc33'
})
})
})
});
//type length or area
var typeSelect={};
//Line start
lis[0].onclick = function(e) {
typeSelect.value = 'length';
typeSelect.check = lis[2].getElementsByTagName("input")[0].checked;
this_.mapmeasure(typeSelect);
};
//Area start
lis[1].onclick = function(e) {
typeSelect.value = 'area';
typeSelect.check = lis[2].getElementsByTagName("input")[0].checked;
this_.mapmeasure(typeSelect);
};
this_.panel.onmouseout = function(e) {
e = e || window.event;
if (!this_.panel.contains(e.toElement || e.relatedTarget)) {
this_.hidePanel();
}
};
ol.control.Control.call(this, {
element: element,
});
};
//继承
ol.inherits(ol.control.MeasureTool, ol.control.Control);
ol.control.MeasureTool.prototype.mapmeasure = function(typeSelect) {
var source = this.sourceMesure;
var vector = this.vectorMesure;
var wgs84Sphere = new ol.Sphere(this.sphereradius);
var sketch;
var helpTooltipElement;
var measureTooltipElement;
var measureTooltip;
var map = this.getMap();
map.addLayer(vector);
map.getViewport().addEventListener('mouseout', function() {
helpTooltipElement.classList.add('hidden');
});
var draw; // global so we can remove it later
var formatLength = function(line) {
var length;
if (typeSelect.check) {
var coordinates = line.getCoordinates();
length = 0;
var sourceProj = map.getView().getProjection();
for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) {
var c1 = ol.proj.transform(coordinates[i], sourceProj, 'EPSG:4326');
var c2 = ol.proj.transform(coordinates[i + 1], sourceProj, 'EPSG:4326');
length += wgs84Sphere.haversineDistance(c1, c2);
}
} else {
var sourceProj = map.getView().getProjection();
var geom = /** @type {ol.geom.Polygon} */(line.clone().transform(
sourceProj, 'EPSG:3857'));
length = Math.round(geom.getLength() * 100) / 100;
// length = Math.round(line.getLength() * 100) / 100;
}
var output;
if (length > 100) {
output = (Math.round(length / 1000 * 100) / 100) +
' ' + 'km';
} else {
output = (Math.round(length * 100) / 100) +
' ' + 'm';
}
return output;
};
var formatArea = function(polygon) {
if (typeSelect.check) {
var sourceProj = map.getView().getProjection();
var geom = /** @type {ol.geom.Polygon} */(polygon.clone().transform(
sourceProj, 'EPSG:4326'));
var coordinates = geom.getLinearRing(0).getCoordinates();
area = Math.abs(wgs84Sphere.geodesicArea(coordinates));
} else {
var sourceProj = map.getView().getProjection();
var geom = /** @type {ol.geom.Polygon} */(polygon.clone().transform(
sourceProj, 'EPSG:3857'));
area = geom.getArea();
// area = polygon.getArea();
}
var output;
if (area > 10000) {
output = (Math.round(area / 1000000 * 100) / 100) +
' ' + 'km<sup>2</sup>';
} else {
output = (Math.round(area * 100) / 100) +
' ' + 'm<sup>2</sup>';
}
return output;
};
var popupcloser = document.createElement('a');
popupcloser.href = 'javascript:void(0);';
popupcloser.classList.add('ol-popup-closer');
function addInteraction() {
var type = (typeSelect.value == 'area' ? 'Polygon' : 'LineString');
draw = new ol.interaction.Draw({
source: source,
type: /** @type {ol.geom.GeometryType} */ (type),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
}),
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.5)',
lineDash: [10, 10],
width: 2
}),
image: new ol.style.Circle({
radius: 5,
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.7)'
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
})
})
})
});
map.addInteraction(draw);
createMeasureTooltip();
createHelpTooltip();
var listener;
draw.on('drawstart',
function(evt) {
// set sketch
sketch = evt.feature;
/** @type {ol.Coordinate|undefined} */
var tooltipCoord = evt.coordinate;
listener = sketch.getGeometry().on('change', function(evt) {
try {
var geom = evt.target;
var output;
if (geom instanceof ol.geom.Polygon) {
output = formatArea(geom);
tooltipCoord = geom.getInteriorPoint().getCoordinates();
} else if (geom instanceof ol.geom.LineString) {
output = formatLength(geom);
tooltipCoord = geom.getLastCoordinate();
}
measureTooltipElement.innerHTML = output;
measureTooltip.setPosition(tooltipCoord);
} catch (e) {
map.removeInteraction(draw);
} finally {
}
});
}, this);
draw.on('drawend',
function() {
measureTooltipElement.appendChild(popupcloser);
measureTooltipElement.className = 'tooltip tooltip-static';
measureTooltip.setOffset([0, -7]);
// unset sketch
sketch = null;
// unset tooltip so that a new one can be created
measureTooltipElement = null;
createMeasureTooltip();
ol.Observable.unByKey(listener);
//end
map.removeInteraction(draw);
// map.getInteractions().item(1).setActive(false);
}, this);
}
function createHelpTooltip() {
if (helpTooltipElement) {
helpTooltipElement.parentNode.removeChild(helpTooltipElement);
}
helpTooltipElement = document.createElement('div');
helpTooltipElement.className = 'tooltip hidden';
}
function createMeasureTooltip() {
if (measureTooltipElement) {
measureTooltipElement.parentNode.removeChild(measureTooltipElement);
}
measureTooltipElement = document.createElement('div');
measureTooltipElement.className = 'tooltip tooltip-measure';
measureTooltip = new ol.Overlay({
element: measureTooltipElement,
offset: [0, -15],
positioning: 'bottom-center'
});
map.addOverlay(measureTooltip);
}
//clear
popupcloser.onclick = function(e) {
map.getOverlays().clear();
vector.getSource().clear();
// map.removeLayer(vector);
};
addInteraction();
};
/**
* Show the MeasureTool.
*/
ol.control.MeasureTool.prototype.showPanel = function() {
if (this.element.className != this.shownClassName) {
this.element.className = this.shownClassName;
}
};
/**
* Hide the MeasureTool.
*/
ol.control.MeasureTool.prototype.hidePanel = function() {
if (this.element.className != this.hiddenClassName) {
this.element.className = this.hiddenClassName;
}
};
/**
* Set the map instance the control is associated with.
* @param {ol.Map} map The map instance.
*/
ol.control.MeasureTool.prototype.setMap = function(map) {
// Clean up listeners associated with the previous map
for (var i = 0, key; i < this.mapListeners.length; i++) {
this.getMap().unByKey(this.mapListeners[i]);
}
this.mapListeners.length = 0;
// Wire up listeners etc. and store reference to new map
ol.control.Control.prototype.setMap.call(this, map);
if (map) {
var this_ = this;
this.mapListeners.push(map.on('pointerdown', function() {
this_.hidePanel();
}));
}
};
/**
* Generate a UUID
* @returns {String} UUID
*
* Adapted from http://stackoverflow.com/a/2117523/526860
*/
ol.control.MeasureTool.uuid = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
}
/**
* @private
* @desc Apply workaround to enable scrolling of overflowing content within an
* element. Adapted from https://gist.github.com/chrismbarr/4107472
*/
ol.control.MeasureTool.enableTouchScroll_ = function(elm) {
if(ol.control.MeasureTool.isTouchDevice_()){
var scrollStartPos = 0;
elm.addEventListener("touchstart", function(event) {
scrollStartPos = this.scrollTop + event.touches[0].pageY;
}, false);
elm.addEventListener("touchmove", function(event) {
this.scrollTop = scrollStartPos - event.touches[0].pageY;
}, false);
}
};
/**
* @private
* @desc Determine if the current browser supports touch events. Adapted from
* https://gist.github.com/chrismbarr/4107472
*/
ol.control.MeasureTool.isTouchDevice_ = function() {
try {
document.createEvent("TouchEvent");
return true;
} catch(e) {
return false;
}
};
三、使用控件
1、js控件引入
<script src="ol3-measuretool-master/measuretool.js"></script>
2、声明控件
new ol.control.MeasureTool( {sphereradius : 6378137}),
其中的参数sphereradius 是用来支持geodesic测量设置球体半径的,可根据不同的模型设置不同的半径大小,默认大小为6378137,在引入时也可以不传入该参数。
NOTE:测量工具中的checkbox选中为使用geodesic测量,未选中为不使用geodesic测量,默认为未选中。
四、总结
通过这几天的研究我发现如果要实现自定义控件,里面有部分函数我们不需要改动,在构造函数那一部分我们需要创建自己定义的标签,其他后面最后的几个函数是不需要改动的,待下次继续完善