在Leaflet或Openlayers地图上绘制S57符号

3
我一直在努力让leaflet(或openlayers)能够显示S-57数据(也称为ENC)符号(称为S-52)在地图上。我知道leaflet可以将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\\ArcGIS\Runtime\Data\ENC\hydrography,并且您可以在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并加载到leaflet中,或者能够从原始数据绘制它们的洞察力。
编辑1:如果您想查看有关如何绘制这些符号的文档,请查看https://iho.int/uploads/user/pubs/standards/s-52/S-52%20PresLib%20Ed%204.0.2%20Part%20I%20Addendum.pdf。其中包含每个符号的标准,要查看我所引用的符号,请搜索“BOYBAR01”。
编辑2:另一个选项是将数据转换为本地瓷砖集,以便可以将其加载到leaflet中。我尝试过一些测试来找到转换数据的软件,但目前还没有发现任何可行的方法。 编辑3: OpenCPN似乎是一个可行的解决方案,但我不确定如何安装它,或者安装后它是如何工作的。如果这条路最终可行,我将在我的nodejs应用程序中使用它。 编辑4: ESRI有一个他们的CIM数据绘图的beta版本,可以在此处找到:https://www.esri.com/arcgis-blog/products/js-api-arcgis/mapping/create-points-lines-and-polygons-using-cimsymbols/ 据我测试,这仅适用于他们的映射平台。由于这是beta版,可能会有一些不起作用的符号,但我还没有看到他们数据库中样式列表中的任何不起作用的符号。唯一的问题是如何让esri的数据与leaflet一起使用。我知道有一个esri-leaflet项目,但现在这仅适用于他们的映射平台。因此,另一个选择是使用此CIM规范使用一些leaflet polylineDecorator之类的东西来绘制数据。唯一的问题是,我该如何做到这一点?
编辑5: 研究了openlayers的工作方式后,我已经添加了openlayers选项。从研究openlayers样式的方式来看,它可以让我更好地控制符号的显示,但是一些像使用画布的笔画图案这样的东西对于很多事情都很有效,然而我不确定如何旋转某些需要朝向多边形内部的数据。点符号就简单了。对此有任何建议将会很有帮助。
2个回答

1

基于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>


你的回答可以通过提供更多支持信息来改善。请编辑以添加进一步细节,例如引用或文档,以便其他人可以确认你的答案是否正确。您可以在帮助中心中找到有关编写好答案的更多信息。 - Ethan
这个方案非常不错,接近我所测试的结果(我没有进行过方向函数的测试)。另一个选择可能会在未来的OpenLayers更新中出现 WebGL矢量渲染器适用于多边形、线和点。我不确定是否有可能在线/多边形上放置图像,但这将极大地提高性能(因为ENC/S57拥有大量特征)。总体上是一篇非常棒的答案! - Manny K SoSo

0

如果您正在寻找图形文件,这个链接可能会引起您的兴趣:OpenSeaMap


1
请添加更多细节。不要只发布链接。 - Charalamm
这些图片适用于点符号,但最大的问题是当我有一个多边形或一条线,并且我需要一个可重复的符号沿着该线。我尝试了https://gis.stackexchange.com/questions/306976/add-image-along-the-linestring/306979,它适用于线,但让它适用于多边形并不总是百分之百可行(因为我将多边形转换为线),因为符号应该面向多边形的内部。 - Manny K SoSo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接