RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 717904
Accepted
Александр
Александр
Asked:2020-09-13 20:28:53 +0000 UTC2020-09-13 20:28:53 +0000 UTC 2020-09-13 20:28:53 +0000 UTC

在网页上制作 Globe+map 的最佳方式是什么?

  • 772

任务是:

  • 旋转的 3d 地球仪,上面标有带有签名的点形式的标签(例如,莫斯科、英格兰等)。
  • 它们必须从城市列表中加载。
  • 当您单击它(地球)时,它应该会转换为具有相同标签的 2d 地图,但正是在此投影中,当您将鼠标悬停在标签上时,会出现标签的签名(例如,以人口和建筑特征的简短列表)。
  • 此外,当您单击标签或城市名称时,它会重定向到所需的页面。

做这个的最好方式是什么?

  1. SVG
  2. 帆布

我以前没有使用过 Canvas 或 SVG。开始学习画布,但发现它无法提供我需要的一切。我找到了很多关于地球仪的例子,但我没有找到正常的文档(也请踢它)。

svg
  • 4 4 个回答
  • 10 Views

4 个回答

  • Voted
  1. Stranger in the Q
    2020-08-10T00:37:05Z2020-08-10T00:37:05Z

    不同公司为不同目的制作了许多开源网络地球仪/地图。我将列出我必须与之共事的人,您可以选择:


    1. 铯

    就源代码和抽象的数量而言,这是一个非常大的地球,它更专注于实时渲染。他可以开箱即用地做很多事情,但是如果没有计算机图形知识,与他一起工作可能会很困难。它经常被使用,你可以找到很多关于它的例子。很好地利用了处理器和显卡的资源,因此它的性能处于最佳状态。

    在此处输入图像描述

    <script src="https://cesiumjs.org/releases/1.36/Build/Cesium/Cesium.js"></script>
    <link rel="stylesheet" href="https://cesiumjs.org/Cesium/Apps/Sandcastle/templates/bucket.css">
    <div id="cesiumContainer" class="fullSize"></div>
    <button style="position:absolute">2d/3d</button>
    <script>
    var extent = Cesium.Rectangle.fromDegrees(20.3,50,40.3,70);
    Cesium.Camera.DEFAULT_VIEW_RECTANGLE = extent;
    Cesium.Camera.DEFAULT_VIEW_FACTOR = 0;
    var viewer = new Cesium.CesiumWidget('cesiumContainer',{
        imageryProvider : Cesium.createOpenStreetMapImageryProvider({
            url: 'https://a.tile.openstreetmap.org/'
        }),
    });
    var scene = viewer.scene;
    var billboards = scene.primitives.add(new Cesium.BillboardCollection());
    var canvas = document.createElement('canvas');
    canvas.width = 16;
    canvas.height = 16;
    var ctx = canvas.getContext('2d');
    ctx.beginPath();
    ctx.arc(5, 5, 5, 0, Cesium.Math.TWO_PI, true);
    ctx.closePath();
    ctx.fillStyle = 'red';
    ctx.fill();
    billboards.add({
      image: canvas,
      position: Cesium.Cartesian3.fromDegrees(30.3,60)
    });
    var labels = new Cesium.LabelCollection();
    labels.add({
        position: Cesium.Cartesian3.fromDegrees(30.3,60),
        text: 'Saint-Petersburg',
        font: '15px Helvetica',
        fillColor: new Cesium.Color(0, 0, 0),
    });
    scene.primitives.add(labels);
    let is2d;
    document.querySelector('button').onclick = () => {
      scene.mode = (is2d = !is2d) ? Cesium.SceneMode.SCENE2D : Cesium.SceneMode.SCENE3D;
    }
    </script>


    1. 网络世界风

    也是一个地球仪,内部更简单,比 Cesium 慢,实现的芯片更少,更易于理解和定制。在一个线程中工作,不加速显卡上的计算,几乎不使用着色器。

    万维网

    let c = document.getElementById('canvasOne');
    c.width = window.innerWidth;
    c.height = window.innerHeight;
    var wwd = new WorldWind.WorldWindow("canvasOne");
    wwd.addLayer(new WorldWind.AtmosphereLayer());
    wwd.addLayer(new WorldWind.BingAerialWithLabelsLayer())
    var markers = new WorldWind.RenderableLayer("Markers")
    wwd.addLayer(markers);
    var attributes = new WorldWind.PlacemarkAttributes();
    var position = new WorldWind.Location(60, 30)
    var placemark = new WorldWind.Placemark(position, /*eyeDistanceScaling*/true, attributes);
    attributes.imageOffset = new WorldWind.Offset(
        WorldWind.OFFSET_FRACTION, 0.3,
        WorldWind.OFFSET_FRACTION, 0.0);
    attributes.labelAttributes.color = WorldWind.Color.YELLOW;
    attributes.labelAttributes.offset = new WorldWind.Offset(
                WorldWind.OFFSET_FRACTION, 0.5,
                WorldWind.OFFSET_FRACTION, 1.0);
    placemark.label = "Saint-Petersburg";
    placemark.altitudeMode = WorldWind.CLAMP_TO_GROUND;
    placemark.eyeDistanceScalingThreshold = 2500000;
    attributes.imageSource = WorldWind.configuration.baseUrl + "images/pushpins/plain-red.png";
    markers.addRenderable(placemark);;
    body {
      margin:0;
      overflow:hidden;
    }
    <script src="https://files.worldwind.arc.nasa.gov/artifactory/web/0.9.0/worldwind.min.js" type="text/javascript"></script>
    <canvas id="canvasOne" width="750" height="750" style="background: black;"></canvas>


    1. D3.js

    非常强大的东西,更适合SVG,但可以以不同的方式呈现,包含许多地理投影及其扩展的数学运算,非常适合创建2D地图和除 mercator / wgs84 之外的各种奢侈投影中的地图,它们几乎用于所有其他映射引擎。

    在此处输入图像描述

    var width = 750, height = 500;
    var options = [
      {name: "Aitoff", projection: d3.geoAitoff()},
      {name: "Albers", projection: d3.geoAlbers().scale(145).parallels([20, 50])},
      {name: "August", projection: d3.geoAugust().scale(60)},
      {name: "Baker", projection: d3.geoBaker().scale(100)},
      {name: "Boggs", projection: d3.geoBoggs()},
      {name: "Bonne", projection: d3.geoBonne().scale(120)},
      {name: "Bromley", projection: d3.geoBromley()},
      {name: "Collignon", projection: d3.geoCollignon().scale(93)},
      {name: "Craster Parabolic", projection: d3.geoCraster()},
      {name: "Eckert I", projection: d3.geoEckert1().scale(165)},
      {name: "Eckert II", projection: d3.geoEckert2().scale(165)},
      {name: "Eckert III", projection: d3.geoEckert3().scale(180)},
      {name: "Eckert IV", projection: d3.geoEckert4().scale(180)},
      {name: "Eckert V", projection: d3.geoEckert5().scale(170)},
      {name: "Eckert VI", projection: d3.geoEckert6().scale(170)},
      {name: "Eisenlohr", projection: d3.geoEisenlohr().scale(60)},
      {name: "Equirectangular (Plate Carrée)", projection: d3.geoEquirectangular()},
      {name: "Hammer", projection: d3.geoHammer().scale(165)},
      {name: "Hill", projection: d3.geoHill()},
      {name: "Goode Homolosine", projection: d3.geoHomolosine()},
      {name: "Kavrayskiy VII", projection: d3.geoKavrayskiy7()},
      {name: "Lambert cylindrical equal-area", projection: d3.geoCylindricalEqualArea()},
      {name: "Lagrange", projection: d3.geoLagrange().scale(120)},
      {name: "Larrivée", projection: d3.geoLarrivee().scale(95)},
      {name: "Laskowski", projection: d3.geoLaskowski().scale(120)},
      {name: "Loximuthal", projection: d3.geoLoximuthal()},
      // {name: "Mercator", projection: d3.geoMercator().scale(490 / 2 / Math.PI)},
      {name: "Miller", projection: d3.geoMiller().scale(100)},
      {name: "McBryde–Thomas Flat-Polar Parabolic", projection: d3.geoMtFlatPolarParabolic()},
      {name: "McBryde–Thomas Flat-Polar Quartic", projection: d3.geoMtFlatPolarQuartic()},
      {name: "McBryde–Thomas Flat-Polar Sinusoidal", projection: d3.geoMtFlatPolarSinusoidal()},
      {name: "Mollweide", projection: d3.geoMollweide().scale(165)},
      {name: "Natural Earth", projection: d3.geoNaturalEarth()},
      {name: "Nell–Hammer", projection: d3.geoNellHammer()},
      {name: "Polyconic", projection: d3.geoPolyconic().scale(100)},
      {name: "Robinson", projection: d3.geoRobinson()},
      {name: "Sinusoidal", projection: d3.geoSinusoidal()},
      {name: "Sinu-Mollweide", projection: d3.geoSinuMollweide()},
      {name: "van der Grinten", projection: d3.geoVanDerGrinten().scale(75)},
      {name: "van der Grinten IV", projection: d3.geoVanDerGrinten4().scale(120)},
      {name: "Wagner IV", projection: d3.geoWagner4()},
      {name: "Wagner VI", projection: d3.geoWagner6()},
      {name: "Wagner VII", projection: d3.geoWagner7()},
      {name: "Winkel Tripel", projection: d3.geoWinkel3()}
    ];
    options.forEach(function(o) {
      o.projection.rotate([0, 0]).center([0, 0]);
    });
    
    var i = 0,
        n = options.length - 1;
    var projection = options[i].projection;
    var path = d3.geoPath(projection);
    var graticule = d3.geoGraticule();
    var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height);
    svg.append("defs").append("path")
        .datum({type: "Sphere"})
        .attr("id", "sphere")
        .attr("d", path);
    svg.append("use")
        .attr("class", "stroke")
        .attr("xlink:href", "#sphere");
    svg.append("use")
        .attr("class", "fill")
        .attr("xlink:href", "#sphere");
    svg.append("path")
        .datum(graticule)
        .attr("class", "graticule")
        .attr("d", path);
        
    d3.json("https://unpkg.com/world-atlas@1.1.4/world/110m.json").then(function(world) {
      svg.insert("path", ".graticule")
          .datum(topojson.feature(world, world.objects.land))
          .attr("class", "land")
          .attr("d", path);
    });
    
    var menu = d3.select("#projection-menu")
        .on("change", change);
    menu.selectAll("option")
        .data(options)
      .enter().append("option")
        .text(function(d) { return d.name; });
    update(options[0])
    function loop() {
      var j = Math.floor(Math.random() * n);
      menu.property("selectedIndex", i = j + (j >= i));
      update(options[i]);
    }
    function change() {
      clearInterval(interval);
      update(options[this.selectedIndex]);
    }
    function update(option) {
      svg.selectAll("path").interrupt().transition()
          .duration(1000).ease(d3.easeLinear)
          .attrTween("d", projectionTween(projection, projection = option.projection))
      d3.timeout(loop, 1000)
    }
    function projectionTween(projection0, projection1) {
      return function(d) {
        var t = 0;
        var projection = d3.geoProjection(project)
            .scale(1)
            .translate([width / 2, height / 2]);
        var path = d3.geoPath(projection);
        function project(λ, φ) {
          λ *= 180 / Math.PI, φ *= 180 / Math.PI;
          var p0 = projection0([λ, φ]), p1 = projection1([λ, φ]);
          return [(1 - t) * p0[0] + t * p1[0], (1 - t) * -p0[1] + t * -p1[1]];
        }
        return function(_) {
          t = _;
          return path(d);
        };
      };
    }
    body {
      background: #fcfcfa;
      height: 500px;
      position: relative;
      width: 960px;
    }
    #projection-menu {
      position: absolute;
      right: 10px;
      top: 10px;
    }
    .stroke {
      fill: none;
      stroke: #000;
      stroke-width: 3px;
    }
    .fill {
      fill: #fff;
    }
    .graticule {
      fill: none;
      stroke: #777;
      stroke-width: .5px;
      stroke-opacity: .5;
    }
    .land {
      fill: #222;
    }
    .boundary {
      fill: none;
      stroke: #fff;
      stroke-width: .5px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <select id="projection-menu"></select>
    <script src="//d3js.org/d3-geo-projection.v1.min.js"></script>
    <script src="//d3js.org/topojson.v2.min.js"></script>


    1. mapbox-gl-js

    这个奇妙的东西的特点是在 2D 和 3D模式下实时渲染矢量图块(在 3D 中它不是地球,而是一个角度的平面地图,就像columbusview在铯中一样),但它也可以与栅格数据源一起使用. 你不能真正改变投影。

    地图盒

    mapboxgl.accessToken = 'pk.eyJ1Ijoib3dlbmxhbWIiLCJhIjoiY2lleWljcnF4MDBiOXQ0bHR0anRvamtucSJ9.t3YnHHqvQZ8Y0MTCNy0NNw';
    
    this.map = new mapboxgl.Map({
        container: 'map', 
        style: 'mapbox://styles/mapbox/streets-v9',
        center: [30.3, 60], 
        zoom: 11 // starting zoom
    });
    
    // Add zoom and rotation controls to the map.
    var nav = new mapboxgl.NavigationControl();
    map.addControl(nav, 'top-right');
    body { 
      margin: 0; 
      padding: 0; 
    }
    
    #map { 
      position: absolute; 
      top: 0; 
      bottom: 0; 
      width: 100%; 
    }
    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.css' rel='stylesheet' /><div id='map'></div>


    1. 传单

    用于2D地图的小型远程库。它不使用直接显卡,重量也很适中,但它拥有庞大的社区和许多不同的扩展。非常适合移动卡和弱电脑。支持各种投影。

    传单

    var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
          
    var osmLayer = new L.TileLayer(osmUrl, {
        maxZoom: 18
    });
    
    var baseMaps = {"OpenStreetMap": osmLayer};
    
    var map = new L.Map('map', {
        center: new L.LatLng(60, 30.3),
        zoom: 12,
        layers: [osmLayer]
    });
    body {
        margin: 0;
        overflow: hidden;
    }
    
    #map {
        position: fixed;
        width: 100%;
        height: 100%;
    }
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css"/>
    <script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js"></script>
    <div id="map"></div>


    1. 开放层

    很棒的传单替代品,更多源代码,更多功能,更多入门门槛,许多示例,对各种投影的正常支持。二维。

    奥尔

    new ol.Map({
    	layers: [new ol.layer.Tile({
            source: new ol.source.OSM()
        })],
    	target: 'map',
    	view: new ol.View({
    		center: ol.proj.fromLonLat([30.3, 60]),
    		zoom: 12
    	})
    });
    body {
        overflow: hidden;
        margin: 0;
    }
    
    #map {
        position: absolute;
        height: 100%;
        width: 100%;
    }
    <script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.2.0/build/ol.js"></script>
    <div id="map"></div>

    • 37
  2. Best Answer
    Stranger in the Q
    2020-05-04T08:04:51Z2020-05-04T08:04:51Z

    Прилагаю Вашему вниманию расширенную версию использования WebWorldWind

    Что тут представлено:

    1. Глобус и плоская карта в проекции меркатора.
    2. Звезды, данные взять из json.
    3. Какой-никакой рендеринг атмосферы.
    4. Информация о городах взята из json, нанесены маркеры на карту и добавлены события на клики к ним.
    5. 通过单击从 Wikipedia 获取信息,对腋下的内容进行了分析

    在此处输入图像描述

    <script src="https://files.worldwind.arc.nasa.gov/artifactory/web/0.9.0/worldwind.min.js" type="text/javascript"></script>
    <canvas id="canvas"></canvas>
    <div>
      <button onclick="switchGlobe()">2d/3d</button>
      <span id="mouseover"></span>
      <span style="float:right;padding-right:15px">use right mouse drag to pan </span>
    </div>
    <div id="popup"></div>
    
    <script>
      // всплывающее окошко
      var popup = document.querySelector('#popup');
      popup.onclick = () => popup.classList.remove('visible')
      
      // инициализируем жвижок, ему нужен идентификатор каныв, на которой он рисует
      var wwd = new WorldWind.WorldWindow("canvas");
      
      wwd.addLayer(new WorldWind.StarFieldLayer());  // добавляем слой со звездами 
      wwd.addLayer(new WorldWind.AtmosphereLayer()); // добавляем слой с атмосферой
      wwd.addLayer(new WorldWind.BingAerialWithLabelsLayer());  // добавляем слой с тайлами от Bing
      
      var markers = new WorldWind.RenderableLayer("Markers"); // создаем пустой слой для маркеров
      wwd.addLayer(markers); // добавляем его
    
      // получаем большой файл с описанием городов и отдираем из них каждый пятисотый =)
      // и добавляем маркеры для них на слой с маркерами
      fetch('https://raw.githubusercontent.com/lutangar/cities.json/master/cities.json')
        .then(r => r.json())
        .then(r => r.forEach((c, i) => i % 500 === 0 &&
          markers.addRenderable(placemark(+c.lat, +c.lng, c.name)))) 
    
      // аттрибуты маркера
      var attributes = new WorldWind.PlacemarkAttributes();
      attributes.imageOffset = new WorldWind.Offset(
        WorldWind.OFFSET_FRACTION, 0.3,  
        WorldWind.OFFSET_FRACTION, 0.0); 
      attributes.labelAttributes.color = WorldWind.Color.YELLOW;
      attributes.labelAttributes.offset = new WorldWind.Offset(
        WorldWind.OFFSET_FRACTION, 0.5,
        WorldWind.OFFSET_FRACTION, 1.0);
        
      // изображение маркера, тут может быть любая картинка
      attributes.imageSource = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='36px' height='56px' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1'  viewBox='0 0 365 560' enable-background='new 0 0 365 560' xml:space='preserve'%3E%3Cg%3E%3Cpath stroke='white' stroke-width='20' fill='%2300AEEF' d='M182.9,551.7c0,0.1,0.2,0.3,0.2,0.3S358.3,283,358.3,194.6c0-130.1-88.8-186.7-175.4-186.9 C96.3,7.9,7.5,64.5,7.5,194.6c0,88.4,175.3,357.4,175.3,357.4S182.9,551.7,182.9,551.7z M122.2,187.2c0-33.6,27.2-60.8,60.8-60.8 c33.6,0,60.8,27.2,60.8,60.8S216.5,248,182.9,248C149.4,248,122.2,220.8,122.2,187.2z'/%3E%3C/g%3E%3C/svg%3E";
    
      // переключение представлений
      function switchGlobe() {
        if (!wwd.flatGlobe) {
          wwd.flatGlobe = new WorldWind.Globe2D(); // создаем и запоминаем 2d глобус
          // устанавливаем ему проекцию меркатора (тут возможны другие проекции)
          wwd.flatGlobe.projection = new WorldWind.ProjectionMercator(); 
          wwd.globe3d = wwd.globe;//  запоминаем 3d глобус (текущий)
        }
        // переключаем по клику представления туда и обратно
        wwd.globe = (wwd.is2d = !wwd.is2d) ? wwd.flatGlobe : wwd.globe3d;
        wwd.redraw();  // нужно позвать перерисовку после переключения
      }
    
      // функция создает новый маркер
      function placemark(lat, lon, name) {
        // местоположение
        var position = new WorldWind.Location(lat, lon); 
        var placemark = new WorldWind.Placemark(position, true, attributes);
        placemark.label = name; // подпись
        // режим обработки высоты (прибить к поверхности)
        placemark.altitudeMode = WorldWind.CLAMP_TO_GROUND; 
        // дистанция с которой начинается масштабирование при отдалении
        placemark.eyeDistanceScalingThreshold = 2500000; 
        return placemark;
      }
    
      // слушатель движения мыши
      wwd.addEventListener("mousemove", function(e) {
        var x = e.clientX,
          y = e.clientY;
        var pickList = wwd.pick(wwd.canvasCoordinates(x, y));
        var o = pickList.objects.find(o => !o.isTerrain);
        document.querySelector('canvas').style.cursor = o ? "pointer" : "default";
        document.querySelector('#mouseover').innerHTML = o ? o.userObject.label : '';
      });
    
      // обработчик кликов
      new WorldWind.ClickRecognizer(wwd, function(recognizer) {
        var x = recognizer.clientX,
          y = recognizer.clientY;
        var pickList = wwd.pick(wwd.canvasCoordinates(x, y));
        var o = pickList.objects.find(o => !o.isTerrain);
        if (!o)
          return;
        fetch("https://en.wikipedia.org/w/api.php?&origin=*&action=opensearch&search=" + o.userObject.label + "&limit=1").then(function(resp) {
          return resp.json()
        }).then(function(data) {
          popup.innerHTML += `<br><iframe src="${data.pop()}"></iframe>`;
        });
        popup.innerHTML = o.userObject.label + "<span id='close'>×</span>";
        popup.classList.add('visible');
      });
    </script>
    
    <style>
      div span,
      div p {
        color: white;
      }
    
      body {
        margin: 0;
        overflow: hidden;
      }
    
      canvas {
        background: black;
        width: 100vw;
        height: 100vh;
      }
    
      div {
        width: 100vw;
        position: absolute;
        top: 0;
        padding: 5px;
        background-color: rgba(0, 0, 0, 0.5)
      }
    
      #popup {
        width: 80vw;
        opacity: 0;
        color: white;
        transition: 300ms;
        pointer-events: none;
        height: 80vh;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%)
      }
    
      #popup.visible {
        opacity: 1;
        pointer-events: all
      }
    
      #popup iframe {
        width: 100%;
        height: calc(100% - 30px);
        border: 0
      }
    
      #close {
        float: right;
        padding-right: 5px;
        cursor: pointer;
        font-size: 20px;
        transition: 300ms
      }
    
      #close:hover {
        color: red
      }
    </style>


    前一个片段的一个变种,这里所有的城市都被折叠成一个kd-tree,并搜索距离半径为 1 度的点击站点最近的城市,然后绘制在地图上......

    <script src="https://files.worldwind.arc.nasa.gov/artifactory/web/0.9.0/worldwind.min.js" type="text/javascript"></script>
    <script src="https://unpkg.com/kdbush@3.0.0/kdbush.min.js"></script>
    <canvas id="canvas"></canvas>
    <div>
      <button onclick="switchGlobe()">2d/3d</button>
      <span id="mouseover"></span>
      <span style="float:right;padding-right:15px">use right mouse drag to pan </span>
    </div>
    <div id="popup"></div>
    
    <script>
      // всплывающее окошко
      var popup = document.querySelector('#popup');
      popup.onclick = () => popup.classList.remove('visible')
      
      // инициализируем жвижок, ему нужен идентификатор каныв, на которой он рисует
      var wwd = new WorldWind.WorldWindow("canvas");
      // добавляем слой со звездами 
      wwd.addLayer(new WorldWind.StarFieldLayer());  
      // добавляем слой с атмосферой
      wwd.addLayer(new WorldWind.AtmosphereLayer());
      // добавляем слой с тайлами от Bing
      wwd.addLayer(new WorldWind.BingAerialWithLabelsLayer());  
      
      // создаем пустой слой для маркеров
      var markers = new WorldWind.RenderableLayer("Markers"); 
      wwd.addLayer(markers); // добавляем его
    
      var index,all;  
        
      // получаем большой файл с описанием городов и отдираем из них каждый пятисотый =)
      // и добавляем маркеры для них на слой с маркерами
      fetch('https://raw.githubusercontent.com/lutangar/cities.json/master/cities.json')
        .then(r => r.json())
        .then(r => index = new KDBush(all=r, p => +p.lat, p => +p.lng))
      
    
    
      // аттрибуты маркера
      var attributes = createPlacemarkAttributes();
    
      // переключение представлений
      function switchGlobe() {
        if (!wwd.flatGlobe) {
          wwd.flatGlobe = new WorldWind.Globe2D(); // создаем и запоминаем 2d глобус
          // устанавливаем ему проекцию меркатора (тут возможны другие проекции)
          wwd.flatGlobe.projection = new WorldWind.ProjectionMercator(); 
            //  запоминаем 3d глобус (текущий)
          wwd.globe3d = wwd.globe;
        }
        // переключаем по клику представления туда и обратно
        wwd.globe = (wwd.is2d = !wwd.is2d) ? wwd.flatGlobe : wwd.globe3d;
        wwd.redraw();  // нужно позвать перерисовку после переключения
      }
    
      // функция создает новый маркер
      function placemark(lat, lon, name) {
        // местоположение
        var position = new WorldWind.Location(lat, lon); 
        var placemark = new WorldWind.Placemark(position, true, attributes);
        placemark.label = name; // подпись
        // режим обработки высоты (прибить к поверхности)
        placemark.altitudeMode = WorldWind.CLAMP_TO_GROUND; 
        // дистанция с которой начинается масштабирование при отдалении
        placemark.eyeDistanceScalingThreshold = 2500000; 
        return placemark;
      }
    
      // слушатель движения мыши
      wwd.addEventListener("mousemove", function(e) {
        var x = e.clientX,
          y = e.clientY;
        var pickList = wwd.pick(wwd.canvasCoordinates(x, y));
        var o = pickList.objects.find(o => !o.isTerrain);
        document.querySelector('canvas').style.cursor = o ? "pointer" : "default";
        document.querySelector('#mouseover').innerHTML = o ? o.userObject.label : '';
      });
    
      // обработчик кликов
      new WorldWind.ClickRecognizer(wwd, function(recognizer) {
        var x = recognizer.clientX,
          y = recognizer.clientY;
        var pickList = wwd.pick(wwd.canvasCoordinates(x, y));
        var o = pickList.objects.find(o => !o.isTerrain);
        if (!o){
            let terrain = pickList.objects.find(o => o.isTerrain).position
            markers.removeAllRenderables();
            let pts = index.within(terrain.latitude, terrain.longitude, 1)
                 .map(i => all[i])
                 .forEach(c=>  markers.addRenderable(placemark(+c.lat, +c.lng, c.name)));
            
                
            return;
        }
          
        fetch("https://en.wikipedia.org/w/api.php?&origin=*&action=opensearch&search=" + o.userObject.label + "&limit=1").then(function(resp) {
          return resp.json()
        }).then(function(data) {
          popup.innerHTML += `<br><iframe src="${data.pop()}"></iframe>`;
        });
        popup.innerHTML = o.userObject.label + "<span id='close'>×</span>";
        popup.classList.add('visible');
      });
        
        function createPlacemarkAttributes(){
              // аттрибуты маркера
      var attributes = new WorldWind.PlacemarkAttributes();
      attributes.imageOffset = new WorldWind.Offset(
        WorldWind.OFFSET_FRACTION, 0.3,  
        WorldWind.OFFSET_FRACTION, 0.0); 
      attributes.labelAttributes.color = WorldWind.Color.YELLOW;
      attributes.labelAttributes.offset = new WorldWind.Offset(
        WorldWind.OFFSET_FRACTION, 0.5,
        WorldWind.OFFSET_FRACTION, 1.0);
        
      // изображение маркера, тут может быть любая картинка
      attributes.imageSource = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10px' height='10px' xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1'  viewBox='0 0 100 100' enable-background='new 0 0 100 100' xml:space='preserve'%3E%3Cg%3E%3Ccircle stroke='white' stroke-width='10' fill='%2300AEEF' cx='50' cy='50' r='40' /%3E%3C/g%3E%3C/svg%3E";
            return attributes;
        }
    </script>
    
    <style>
      div span,
      div p {
        color: white;
      }
    
      body {
        margin: 0;
        overflow: hidden;
      }
    
      canvas {
        background: black;
        width: 100vw;
        height: 100vh;
      }
    
      div {
        width: 100vw;
        position: absolute;
        top: 0;
        padding: 5px;
        background-color: rgba(0, 0, 0, 0.5)
      }
    
      #popup {
        width: 80vw;
        opacity: 0;
        color: white;
        transition: 300ms;
        pointer-events: none;
        height: 80vh;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%)
      }
    
      #popup.visible {
        opacity: 1;
        pointer-events: all
      }
    
      #popup iframe {
        width: 100%;
        height: calc(100% - 30px);
        border: 0
      }
    
      #close {
        float: right;
        padding-right: 5px;
        cursor: pointer;
        font-size: 20px;
        transition: 300ms
      }
    
      #close:hover {
        color: red
      }
    </style>

    • 11
  3. Konstantin Emelyanov
    2020-05-04T06:53:59Z2020-05-04T06:53:59Z

    Если нужен именно глобус а не карта, то можно использовать такую связку: Leaflet + WebGL Earth 2.0, которая расширяет базовые возможности Leaflet и добавляет 3D
    在此处输入图像描述

    Плюсом такого решения является то, что при таком подходе доступны все инструменты Leaflet. К примеру для работы с метками, их кластеризация, обработка кликов, отображение атрибутивной информации.
    在此处输入图像描述 Плюс появляется возможность показа в 3D. Поддерживает различные стили от MapBox:
    在此处输入图像描述

    Также есть возможность создавать собственные стили.
    Данные могут быть получены с серверов MapBox или с собственных гео-серверов, а также из файлов GeoJson или csv.
    Ссылки:
    GitHub WebGL Earth 2.0 https://github.com/webglearth/webglearth
    Документация и API WebGL Earth 2.0 http://www.webglearth.org
    Пример WebGL Earth 2.0 https://www.webglearth.com
    Документация API и примеры Leaflet https://leafletjs.com

    • 6
  4. Stranger in the Q
    2020-05-13T05:37:07Z2020-05-13T05:37:07Z

    一个单独的帖子专门讨论铯。

    在这里,它topojson以俄罗斯联邦主体的边界和集群城市的边界绘制。

    在此处输入图像描述

    var cities = 'https://raw.githubusercontent.com/lutangar/cities.json/master/cities.json';
    
    var russia = 'https://raw.githubusercontent.com/logvik/d3_russian_map/master/map_assets/russia_mercator.json';
    
    setInitialView(50, 60);
    var imagery = Cesium.createDefaultImageryProviderViewModels().slice(3);
    var viewer = new Cesium.Viewer("cesiumContainer", {
        timeline: false, animation: false, geocoder: false,
        imageryProviderViewModels: imagery
    });
    let ds = new Cesium.CustomDataSource('billboards');
    viewer.dataSources.add(ds);
    clusteringSupport(ds);
    var canvas = createCityImage();
    
    
    
    viewer.dataSources.add(Cesium.GeoJsonDataSource.load(russia, {
      stroke: Cesium.Color.RED,
      fill: Cesium.Color.TRANSPARENT,
      strokeWidth: 5,
      markerSymbol: '?'
    }));
    
    fetch(cities).then(r => r.json())
        .then(r => r
              .filter((c, i) => c.country === "RU")
             // .filter((c, i) => i % 100 === 0)
                    .forEach(addCity))
    
    addMouseover();
    ////
    var pickedObject;
    
    window.addEventListener('click', function() {
        pickedObject&&console.log(pickedObject.id._name)
    })
    
    function addMouseover() {
        new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
            .setInputAction(function(movement) {
                pickedObject = viewer.scene.pick(movement.endPosition);
            }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    }
    
    function setInitialView(lon, lat) {
        Cesium.Camera.DEFAULT_VIEW_RECTANGLE = 
            Cesium.Rectangle.fromDegrees(lon-10, lat-10, lon+10, lat+10);
        Cesium.Camera.DEFAULT_VIEW_FACTOR = 0;
    }
    
    function addCity(city) {
        var pos = Cesium.Cartesian3.fromDegrees(+city.lng, +city.lat);
        ds.entities.add({
            name : city.name,
            position: pos,
            description: `<a target="_blank" href='${"https://en.wikipedia.org/w/api.php?&origin=*&action=opensearch&search=" + city.name + "&limit=1"}'>${city.name}</a>`,
            billboard: {
                image: canvas 
            },
            label : {
                text : city.name,
                font : '14pt monospace',
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                outlineWidth : 2,
                verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
                pixelOffset : new Cesium.Cartesian2(0, -9)
            }
        })
    }
                  
    function clusteringSupport(ds) {
        
        ds.clustering.enabled = true;
        ds.clustering.pixelRange = 35;
        
        var pinBuilder = new Cesium.PinBuilder();
        var amounts = [2000, 1000, 500, 100, 50, 10, 9, 8, 7, 6, 5, 4, 3, 2].map(i => ({
            i: i, image: pinBuilder.fromText(i + (i>9 ? "+" : ""),                   
            Cesium.Color[i>100?'RED':i>10?'BLUE':'GREEN'], 38).toDataURL()
        }));
    
        ds.clustering.clusterEvent.addEventListener(function(clusteredEntities, cluster) {
            
            cluster.label.show = false;
            cluster.billboard.show = true;
            cluster.billboard.id = cluster.label.id;
            cluster.billboard.verticalOrigin = Cesium.VerticalOrigin.BOTTOM;
            
            for (var i=0; i<amounts.length; i++) {
                if (clusteredEntities.length >= amounts[i].i) {
                    cluster.billboard.image = amounts[i].image; 
                    break;
                }       
            }
        });
    }
    
    function createCityImage(){
        var canvas = document.createElement("canvas");
        canvas.width = 16;
        canvas.height = 16;
        var ctx = canvas.getContext("2d");
        ctx.beginPath();
        ctx.arc(5, 5, 5, 0, Cesium.Math.TWO_PI, true);
        ctx.closePath();
        ctx.fillStyle = "red";
        ctx.fill();
        return canvas;
    }
    .cesium-widget-credits{
      display:none !important; /* only for example on ru.so*/
    }
    <script src="https://cesiumjs.org/releases/1.36/Build/Cesium/Cesium.js"></script>
    <link rel="stylesheet" href="https://cesiumjs.org/Cesium/Apps/Sandcastle/templates/bucket.css">
    <div id="cesiumContainer" class="fullSize"></div>

    PS:这个地球仪有足够的功能,我有时会补充这个帖子......

    • 2

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5