web-dev-qa-db-ja.com

リーフレットjsを使用してマップ上にdiv要素を正確に配置するにはどうすればよいですか?

私はこのいくつかの方法を試しましたが、それを機能させることができませんでした。複数のタイムゾーン内で地図の上部に時計を配置したいと思います。時計を作成するためのJavaScriptがあり、時計をdiv要素に配置します。

これが私が試したものです:

  1. 0,0座標でポイントを作成します。
  2. この時点から、containerPointToLatLngを使用してマップ上部の緯度値を取得します。
  3. 上記の緯度と経度を使用してLatLngを作成します。
  4. このLatLngをPointに変換し、このポイントからx,yを使用してdiv要素を配置します。

ページが最初にレンダリングされたときと本文のサイズ変更時の両方でロジックを実行します。ただし、ブラウザウィンドウのサイズを変更すると、時計の位置が正しくなりません。

助言がありますか?

18
user3137607

OPの正確な問題、つまりブラウザウィンドウのサイズが変更されたときに時計を再配置する方法(したがって、マップコンテナの寸法が変更されている可能性があります)に答えるには、おそらくマップの時計の位置を再計算する必要があります "resize" イベント。

ただし、OPが時計をマップコンテナの子として配置したのか、ページDOMツリーの別の場所に配置したのかは明確ではありません。

マップの子として配置する方がはるかに簡単であるため、その位置は常にマップコンテナに対して相対的です。

OPが最初に尋ねたのは何ですか?

私が元の望ましい結果を正しく理解している場合、OPは特定の地理的位置(コメントによると、その場合はトロント市[UTC -5])の上に時計(またはその他の情報)をオーバーレイしたいと考えています。

ただし、情報コンテナは、ベースマップの固定位置、つまり正確な地理的調整ポイント(マーカーやポップアップのように)ではなく、コントロールと同様にマップコンテナの上部に配置する必要があります(したがって、iH8の元の回答) )。

ただし、マップコンテナ内で完全に固定するのではなく、都市(または指定された地理座標)とともに水平方向に移動する必要があります。したがって、iH8の回答に対するOPのコメント。

したがって、インタラクティブな(ナビゲート可能な)マップと「UTC-5」ヘッダーが時計(またはその他の情報)に置き換えられていることを除いて、 そのサイト に似ているように聞こえます。それ)そしてトロントに水平に続いて。

言い換えると、時計は特定の垂直線、つまり経度/子午線に配置する必要があります。

残念ながら、質問が投稿されてから2年半経っても、そのような機能を提供するLeafletプラグインはまだありません(少なくとも Leafletプラグインページ 内)。

ユースケースを高度に拡大されたマップに拡張…

そうは言っても、ユーザーが都市に高度にズームインできる可能性があるという事実を考えると(OPは最大ズームレベルを指定していません)、その時計を正確な経度に水平に従わせるのはあまり良いユーザーエクスペリエンスではないかもしれません。たとえば、トロントの重心/市庁舎/特定の場所を追跡できます。ユーザーが別の市区町村にズームインすると、時計は表示されなくなりますが、ユーザーはトロント市の一部を表示しています…

そのユースケースを拡張するには、時計が適用される領域に表示される可能性が非常に高くなります。つまり、マップビューポートが関連するタイムゾーンと交差するとすぐに表示されます。 。

ユースケースを高度にズームアウトされたマップに拡張…

OPで詳しく説明されていないもう1つのポイントは、マップビューポートに異なるタイムゾーンの場所が表示されている場合はどうすればよいですか? 上記のサイト には、表示されているタイムゾーンごとに1つのヘッダーがあります。これは、取得できる最も完全な情報のようです。

ただし、Leafletではレベル0にズームアウトできるため、全世界(つまり、基本的に24のタイムゾーン/ Wikipediaによると実際には39 、夏時間の潜在的な影響を含まない-DST)は次のように表されます。 256ピクセル幅で、各クロックを関連するタイムゾーンに垂直に揃える必要がある場合、これらすべてのクロックを収める余地はほとんどありません。

今のところ、クロックがオーバーラップしてもかまわないと仮定しましょう。

さらにカスタムケース…

しかし、OPは、全世界ではなく、特定の場所にのみ時計を表示したいと考えていた可能性があります。 OPは、時計が異なるとは言っていませんでした(同じタイムゾーンの都市の時計を使用することもできますが、これらの時計を都市の隣に配置する方が面白いかもしれませんが、緯度と同じくらいでも同じ子午線上の2つの都市の場合のように、時計が関連付けられている都市を見つけるのは簡単ですが、その場合は、L.divIconのマーカーで十分です)。

したがって、カスタムケースは、公式のタイムゾーンではなく、開発者が指定した領域を考慮することです。

そのため、緯度を忘れて、マップビューポートと交差する限り、時計をエリアの垂直方向に揃えようとします。

一般的なソリューションの説明

したがって、一般的な解決策は、アプリケーション開発者がHTML要素の配列を指定できるようにすることであるように思われます。各要素は経度の範囲に関連付けられています(領域/ポリゴンの場合もあります)。

タイムゾーンのユースケースは、指定されたエリアが単にタイムゾーンからのものである特定のケースになります。

次に、各要素は、関連する領域がビューポートと交差する場合にのみ表示されます(したがって、緯度範囲が表示されていないときに要素を非表示にする可能性があります)。

ポジショニングに関しては、以下を選択しましょう。

  • OPで言及されているように、マップコンテナの上部(コントロールと同様)。
  • ビューポートと関連領域の交差点内で水平方向の中央に配置されます。

HTML:

<div id="map"></div>

<div id="clockToronto" class="clock leaflet-control">Clock here for Toronto</div>
<div id="clockBurlington" class="clock leaflet-control">Clock here for Burlington</div>

CSS:

.clock {
  width: 150px;
  text-align: center;
  position: absolute;
  border: 1px solid black;
}

#clockToronto {
  background-color: yellow;
}

#clockBurlington {
  background-color: orange;
}

JavaScript:

var map = L.map("map").setView([43.7, -79.4], 10);

// Application specific.
var clockTorontoElement = L.DomUtil.get("clockToronto"),
    clockBurlingtonElement = L.DomUtil.get("clockBurlington"),
    zones = [
      {
        element: clockTorontoElement, // Using the HTML Element for now.
        width: parseInt(window.getComputedStyle(clockTorontoElement, null).getPropertyValue("width"), 10),
        area: L.latLngBounds([43.58, -79.64], [43.86, -79.10]) // Using L.latLngBounds for now.
      },
      {
        element: clockBurlingtonElement, // Using the HTML Element for now.
        width: parseInt(window.getComputedStyle(clockBurlingtonElement, null).getPropertyValue("width"), 10),
        area: L.latLngBounds([43.28, -79.96], [43.48, -79.71]) // Using L.latLngBounds for now.
      }
    ];

// Initialization
var controlsContainer = map._container.getElementsByClassName("leaflet-control-container")[0],
    firstCorner = controlsContainer.firstChild,
    mapContainerWidth;

map.on("resize", setMapContainerWidth);
setMapContainerWidth();

// Applying the zones.
for (var i = 0; i < zones.length; i += 1) {
  setZone(zones[i]);
}

function setZone(zoneData) {
  // Visualize the area.
  L.rectangle(zoneData.area).addTo(map);
  console.log("width: " + zoneData.width);

  controlsContainer.insertBefore(zoneData.element, firstCorner);
  map.on("move resize", function () {
    updateZone(zoneData);
  });
  updateZone(zoneData);
}

function updateZone(zoneData) {
  var mapBounds = map.getBounds(),
      zoneArea = zoneData.area,
      style = zoneData.element.style;

  if (mapBounds.intersects(zoneArea)) {
    style.display = "block";

    var hcenterLng = getIntersectionHorizontalCenter(mapBounds, zoneArea),
        hcenter = isNaN(hcenterLng) ? 0 : map.latLngToContainerPoint([0, hcenterLng]).x;

    // Update Element position.
    // Could be refined to keep the entire Element visible, rather than cropping it.
    style.left = (hcenter - (zoneData.width / 2)) + "px";
  } else {
    style.display = "none";
  }
}

function getIntersectionHorizontalCenter(bounds1, bounds2) {
  var west1 = bounds1.getWest(),
      west2 = bounds2.getWest(),
      westIn = west1 < west2 ? west2 : west1,
      east1 = bounds1.getEast(),
      east2 = bounds2.getEast(),
      eastIn = east1 < east2 ? east1 : east2;

  return (westIn + eastIn) / 2;
}

function setMapContainerWidth() {
  mapContainerWidth = map.getSize().x;
}

enter image description here

ライブデモ: http://plnkr.co/edit/V2pvcva5S9OZ2N7LlI8r?p=preview

14
ghybs

通常、L.Controlを使用してカスタムコントロールを作成し、それをコントロールレイヤーに追加できます。そうすると、地図のサイズを変更するときにリーフレットが配置を処理します。 L.Controlのリファレンスを見てください: http://leafletjs.com/reference.html#control

リファレンスにカスタムコントロールの例があります: http://leafletjs.com/reference.html#icontrol 他の例を見たい場合は、多くのカスタムコントロールの1つをチェックしてください。 L.Controlの実装方法を確認するプラグイン: http://leafletjs.com/plugins.html (コントロールとインタラクションの下)

L.Controlの唯一の欠点は、コントロールを垂直方向または水平方向の中央に配置できないことです。使用できるのは、左上、右上、左下、右下のみです。

2
iH8