[英]Drawing S57 symbols onto Leaflet or Openlayers map
我一直在努力让传单(或 openlayers)能够在地图上显示 S-57 数据(也称为 ENC)符号(称为 S-52)。 我知道传单可以将 svg 数据放在地图上,但除了两种格式之外,我还没有看到这些符号被用作其他任何东西。 (可以在此处找到这些符号的示例: https ://github.com/OpenCPN/OpenCPN/blob/master/data/s57data/rastersymbols-day.png)
第一个示例来自以下链接: https ://raw.githubusercontent.com/OpenCPN/OpenCPN/master/data/s57data/chartsymbols.xml
<symbol RCID="1268">
<name>BOYBAR01</name>
<description>barrel buoy, paper-chart</description>
<bitmap width="19" height="14">
<distance min="0" max="0" />
<pivot x="11" y="12" />
<origin x="0" y="0" />
<graphics-location x="926" y="10" />
</bitmap>
<color-ref>CCHBLK</color-ref>
<vector width="615" height="440">
<distance min="0" max="0" />
<pivot x="1500" y="1500" />
<origin x="1145" y="1110" />
<HPGL>SPC;PU1500,1500;SW2;CI50;PU1200,1500;PD1200,1345;PD1210,1280;PD1245,1210;PU1290,1165;PD1335,1140;PD1390,1120;PD1435,1110;PD1480,1110;PD1530,1120;PD1590,1155;PD1635,1185;PD1660,1215;PD1685,1265;PD1700,1310;PD1705,1345;PD1705,1500;PU1275,1175;PD1310,1230;PD1345,1280;PD1365,1335;PD1380,1410;PD1390,1455;PD1390,1500;PU1545,1500;PD1760,1500;PU1245,1210;PD1290,1165;PU1445,1500;PD1145,1500;</HPGL>
</vector>
<definition>V</definition>
</symbol>
在上述情况下,路径被编码为 HPGL。 我尝试将 HPGL 转换为 SVG,但转换器要么失败,要么颠倒打印。
另一种格式取自 S-52x.stylx(来自 ArcGIS,如果您安装了 ENC 查看器扩展程序,您可以在此处找到:C:\Users\<yourUsername>\ArcGIS\Runtime\Data\ENC\hydrography 和您可以使用 DB Browser for SQLite 在表 SymbolInfo 下加载它)
{
"type": "CIMPointSymbol",
"symbolLayers": [
{
"type": "CIMVectorMarker",
"enable": true,
"anchorPoint": {
"x": 1.346457,
"y": -4.818898,
"z": 0.000000
},
"anchorPointUnits": "Absolute",
"dominantSizeAxis3D": "Z",
"offsetX": 0.000000,
"rotateClockwise": true,
"size": 12.472441,
"billboardMode3D": "None",
"frame": {
"xmin": -10.062992,
"ymin": -1.417323,
"xmax": 7.370079,
"ymax": 11.055118
},
"markerGraphics": [
{
"type": "CIMMarkerGraphic",
"geometry": {
"paths": [
[
[
1.417323,
0.000000
],
[
1.403167,
-0.199814
],
[
1.360984,
-0.395636
],
[
1.291614,
-0.583555
],
[
1.196445,
-0.759818
],
[
1.077376,
-0.920904
],
[
0.936787,
-1.063595
],
[
0.777486,
-1.185040
],
[
0.602654,
-1.282814
],
[
0.415785,
-1.354964
],
[
0.220610,
-1.400048
],
[
0.021028,
-1.417167
],
[
-0.178973,
-1.405977
],
[
-0.375400,
-1.366704
],
[
-0.564328,
-1.300130
],
[
-0.741984,
-1.207586
],
[
-0.904818,
-1.090921
],
[
-1.049579,
-0.952464
],
[
-1.173374,
-0.794982
],
[
-1.273731,
-0.621621
],
[
-1.348646,
-0.435842
],
[
-1.396621,
-0.241358
],
[
-1.416699,
-0.042052
],
[
-1.408478,
0.158094
],
[
-1.372123,
0.355082
],
[
-1.308360,
0.544976
],
[
-1.218462,
0.723986
],
[
-1.104225,
0.888533
],
[
-0.967932,
1.035332
],
[
-0.812304,
1.161450
],
[
-0.640450,
1.264369
],
[
-0.455803,
1.342031
],
[
-0.262052,
1.392886
],
[
-0.063066,
1.415919
],
[
0.137179,
1.410669
],
[
0.334685,
1.377240
],
[
0.525505,
1.316301
],
[
0.705828,
1.229069
],
[
0.872052,
1.117286
],
[
1.020857,
0.983186
],
[
1.149271,
0.829446
],
[
1.254727,
0.659138
],
[
1.335121,
0.475664
],
[
1.388845,
0.282689
],
[
1.417323,
0.000000
]
],
[
[
-8.503937,
0.000000
],
[
-8.503937,
4.393701
],
[
-8.220472,
6.236220
],
[
-7.228346,
8.220472
]
],
[
[
-5.952756,
9.496063
],
[
-4.677165,
10.204724
],
[
-3.118110,
10.771654
],
[
-1.842520,
11.055118
],
[
-0.566929,
11.055118
],
[
0.850394,
10.771654
],
[
2.551181,
9.779528
],
[
3.826772,
8.929134
],
[
4.535433,
8.078740
],
[
5.244094,
6.661417
],
[
5.669291,
5.385827
],
[
5.811024,
4.393701
],
[
5.811024,
0.000000
]
],
[
[
-6.377953,
9.212598
],
[
-5.385827,
7.653543
],
[
-4.393701,
6.236220
],
[
-3.826772,
4.677165
],
[
-3.401575,
2.551181
],
[
-3.118110,
1.275591
],
[
-3.118110,
0.000000
]
],
[
[
1.275591,
0.000000
],
[
7.370079,
0.000000
]
],
[
[
-7.228346,
8.220472
],
[
-5.952756,
9.496063
]
],
[
[
-1.559055,
0.000000
],
[
-10.062992,
0.000000
]
]
]
},
"symbol": {
"type": "CIMLineSymbol",
"symbolLayers": [
{
"type": "CIMSolidStroke",
"enable": true,
"capStyle": "Round",
"joinStyle": "Bevel",
"lineStyle3D": "Tube",
"miterLimit": 10,
"width": 2,
"color": [
0,
0,
0,
255
]
}
]
}
}
],
"respectFrame": true
}
],
"haloSize": 1,
"scaleX": 1,
"angleAlignment": "Display"
}
深入了解如何将它们转换为 svg,然后我可以将其加载到传单中,或者能够从原始数据中提取它们。
编辑 1:如果您想查看有关如何绘制这些符号的文档,请查看https://iho.int/uploads/user/pubs/standards/s-52/S-52%20PresLib%20Ed%204.0.2%20Part %20I%20Addendum.pdf包含每个符号的标准,要查看我引用的搜索“BOYBAR01”
编辑 2:另一个选项可能是将数据转换为本地图块集,以便我可以将其加载到传单中。 我已经尝试了几个测试来找到转换数据的软件,但到目前为止我还没有发现任何东西(至少有效)。
编辑 3: OpenCPN 似乎是一个可行的解决方案,但是我不确定如何安装它,或者它在安装后如何工作。 如果这条路线最终正常工作,我将最终在我的 nodejs 应用程序中使用。
编辑 4: ESRI 有一个测试版的 CIM 数据绘图在这里找到: https ://www.esri.com/arcgis-blog/products/js-api-arcgis/mapping/create-points-lines-and-polygons-using -cimsymbols/据我测试,这只适用于他们的映射平台。 由于这是测试版,可能有一些符号不起作用,但我还没有从他们的数据库中的样式列表中看到任何不起作用的符号。 唯一的问题是如何从 esri 获取这些数据以使用传单。 我知道有一个 esri-leaflet 项目,但目前这仅适用于他们的地图平台。 因此,另一种选择是使用此 CIM 规范,使用一些传单 polylineDecorator 或类似的东西来绘制数据。 唯一的问题是,我该怎么做呢?
编辑 5:研究 openlayers 的工作方式我添加了 openlayers 的选项。 通过研究 openlayers 样式的工作方式,它可以让我更好地控制符号的显示方式,但是像使用画布的 Stroke Pattern 之类的东西可以很好地处理很多事情,但是我不确定如何旋转一些需要向内朝向多边形的数据。 点符号很简单。 对此的任何建议都会有所帮助。
基于 Mike Nunn 的解决方案 ( https://gis.stackexchange.com/questions/306976/add-image-along-the-linestring/306979 ),以下代码可能会解决您的问题。
// Symbol var img = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAADsQAAA7EB9YPtSQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHzSURBVFiF7ZY9SBxBFMd/s7uHGIKoB4paJJIyJNjETjkRG7FIE5MlEEQJRAiEC1xhIG0QRIQIdp6gnQgipEqRSkgXDGiXwAUEOZDoJSx+3Mez8LKep3s6c3oWuX83O/P++5v3dt4sVFXVDUuZBk7yUpqoB8Bjn1dMG3lZJkEuEQlT549rqeE9rlQM4CHt2AWhFop7tJpYmQGMMaey5Pxxlhzf+FEZgEG65Tk9ckimwERxQJqndGuXQfvDWSBW8iUb/GKcxUv7OroACZIoFA42bYQBSOGxiwfAJttafsbHEE6yscJXllit3DEsloNtHFs2gCDYNwkA4JRhcyUA15IBl8ilz/S1ZOA+dxhn+EIIhSqZAZeIzJfoHYEAW/ymjTBxohdCBGXgA0PSzyMg2CIQIMkOGbKEcFggJqMMBLqEzulncd5Ka75RbfNHH2CJVWXlpwWhneYzayS/s2KAJ3RJCBsbC0FIkNQHOJ48bm4KRQuNvOPZqSz8AygsgUtEBuj0xwrFR1YCu2TJ9jlPTF4woYbok146/OdpsqTwaOA2NhZ7HJImQx23/DUpPF4zo2aJyghTZgDFihOV8+pdLJ0b0egCecNjecBdQjh+mf6yxzoJZvik5Vn2bZhDWOMnUywbeWn/DxTqC98BmONzWRup6v/WETwrgqYBFtclAAAAAElFTkSuQmCC" // Helpers function splitLineString(geometry, minSegmentLength, options) { function calculatePointsDistance(coord1, coord2) { var dx = coord1[0] - coord2[0]; var dy = coord1[1] - coord2[1]; return Math.sqrt(dx * dx + dy * dy); } function calculateSplitPointCoords(startNode, nextNode, distanceBetweenNodes, distanceToSplitPoint) { var d = distanceToSplitPoint / distanceBetweenNodes; var x = nextNode[0] + (startNode[0] - nextNode[0]) * d; var y = nextNode[1] + (startNode[1] - nextNode[1]) * d; return [x, y]; } function calculateAngle(startNode, nextNode, alwaysUp) { var x = (startNode[0] - nextNode[0]); var y = (startNode[1] - nextNode[1]); var angle = alwaysUp ? Math.atan(x / y) : Math.atan2(x, y); return angle - Math.PI / 2 * orientation } var splitPoints = []; var coords = geometry.getCoordinates(); var coordIndex = 0; var startPoint = coords[coordIndex]; var nextPoint = coords[coordIndex + 1]; var angle = calculateAngle(startPoint, nextPoint, options.alwaysUp); var n = Math.ceil(geometry.getLength() / minSegmentLength); var segmentLength = geometry.getLength() / n; var currentSegmentLength = options.midPoints ? segmentLength / 2 : segmentLength; for (var i = 0; i <= n; i++) { var distanceBetweenPoints = calculatePointsDistance(startPoint, nextPoint); currentSegmentLength += distanceBetweenPoints; if (currentSegmentLength < segmentLength) { coordIndex++; if (coordIndex < coords.length - 1) { startPoint = coords[coordIndex]; nextPoint = coords[coordIndex + 1]; angle = calculateAngle(startPoint, nextPoint, options.alwaysUp); i--; continue; } else { if (!options.midPoints) { var splitPointCoords = nextPoint; if (!options.extent || ol.extent.containsCoordinate(options.extent, splitPointCoords)) { splitPointCoords.push(angle); splitPoints.push(splitPointCoords); } } break; } } else { var distanceToSplitPoint = currentSegmentLength - segmentLength; var splitPointCoords = calculateSplitPointCoords(startPoint, nextPoint, distanceBetweenPoints, distanceToSplitPoint); startPoint = splitPointCoords.slice(); if (!options.extent || ol.extent.containsCoordinate(options.extent, splitPointCoords)) { splitPointCoords.push(angle); splitPoints.push(splitPointCoords); } currentSegmentLength = 0; } } return splitPoints; } function calculateOrientation(coords) { var area = 0; for (var i = 0; i < coords.length; i++) { const addX = coords[i][0]; const addY = coords[i === coords.length - 1 ? 0 : i + 1][1]; const subX = coords[i === coords.length - 1 ? 0 : i + 1][0]; const subY = coords[i][1]; area += (addX * addY * 0.5) - (subX * subY * 0.5); } return Math.sign(area); } // StyleFunction var styleFunction = function(feature, resolution) { var styles = [ new ol.style.Style({ stroke: new ol.style.Stroke({ color: "darkmagenta", width: 2, lineDash: [4] }) }) ]; var size = 64; var mapSize = map.getSize(); var extent = map.getView().calculateExtent([mapSize[0] + (size * 2), mapSize[1] + (size * 2)]); var splitPoints = splitLineString(feature.getGeometry(), size * 2 * resolution, { alwaysUp: false, midPoints: true, extent: extent }); splitPoints.forEach(function(point, index) { styles.push(new ol.style.Style({ geometry: new ol.geom.Point([point[0], point[1]]), image: new ol.style.Icon({ src: img, scale: 0.8, rotation: point[2] }) })); }); return styles; } // Layer var raster = new ol.layer.Tile({ source: new ol.source.OSM() }); var vectorSource = new ol.source.Vector(); var vector = new ol.layer.Vector({ source: vectorSource, style: styleFunction }); // Map var map = new ol.Map({ layers: [raster, vector], target: "map", view: new ol.View({ center: ol.proj.fromLonLat([7.5, 54.0]), zoom: 8 }) }); // Interaction var draw = new ol.interaction.Draw({ source: vectorSource, type: "LineString" }); var orientation; draw.on("drawend", function(event) { var featureGeom = event.feature.getGeometry(); if (featureGeom) { var featureCoords = featureGeom.getCoordinates(); if (featureCoords.length > 0) { featureCoords.push(featureCoords[0]) featureGeom.setCoordinates(featureCoords); orientation = calculateOrientation(featureCoords) // map.removeInteraction(draw); } } }) map.addInteraction(draw);
html, body, .map { width: 100%; height: 100%; overflow: hidden; }
<link href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css" rel="stylesheet" /> <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script> <div id="map" class="map" tabindex="0"></div>
如果您正在寻找图形文件,可能会对这个链接感兴趣: OpenSeaMap
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.