You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

675 lines
33 KiB
HTML

<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
<script>
var gaProperty = 'UA-2577926-1';
// Disable tracking if the opt-out cookie exists.
var disableStr = 'ga-disable-' + gaProperty;
if (document.cookie.indexOf(disableStr + '=true') > -1) {
window[disableStr] = true;
}
function gaOptout() {
document.cookie = disableStr + '=true; expires=Thu, 31 Dec 2099 23:59:59 UTC; path=/';
window[disableStr] = true;
}
function gaOptoutRevoke() {
document.cookie = disableStr + '=false; expires=Thu, 31 Dec 2099 23:59:59 UTC; path=/';
window[disableStr] = false;
}
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-2577926-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-2577926-1', { 'anonymize_ip': true });
</script>
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.css" />
<script src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.1.0/cookieconsent.min.js"></script>
<script src="https://unpkg.com/lz-string@1.4.4/libs/lz-string.min.js"></script>
<script>
window.addEventListener("load", function() {
window.cookieconsent.initialise({
'palette': {
'popup': {
'background': '#eaf7f7',
'text': '#5c7291'
},
'button': {
'background': '#56cbdb',
'text': '#ffffff'
}
},
'theme': 'edgeless',
'type': 'opt-out',
'onInitialise': function (status) {
if (!this.hasConsented()) {
gaOptout()
}
},
'onStatusChange': function(status, chosenBefore) {
if (!this.hasConsented()) {
gaOptout()
}
},
'onRevokeChoice': function() {
gaOptoutRevoke()
}
})
});
</script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" type="text/css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" type="text/css">
<link rel="stylesheet" href="./resources/prism/prism-1.20.0.css" type="text/css">
<link rel="stylesheet" href="./css/ol.css" type="text/css">
<link rel="stylesheet" href="./resources/layout.css" type="text/css">
<script src="https://unpkg.com/elm-pep"></script>
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL,TextDecoder"></script>
<title>Tracing around a polygon</title>
</head>
<body>
<header class="navbar navbar-expand-sm navbar-dark mb-3 py-0" role="navigation">
<a class="navbar-brand" href="https://openlayers.org/"><img src="./resources/logo-70x70.png" alt="">&nbsp;OpenLayers</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#olmenu" aria-controls="olmenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- menu items that get hidden below 768px width -->
<nav class="collapse navbar-collapse" id="olmenu">
<ul class="nav navbar-nav ml-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="docdropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Docs</a>
<div class="dropdown-menu dropdown-menu-right mb-3" aria-labelledby="docdropdown">
<a class="dropdown-item" href="../doc/">Docs</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="../doc/quickstart.html"><i class="fa fa-check fa-fw mr-2 fa-lg"></i>Quick Start</a>
<a class="dropdown-item" href="../doc/faq.html"><i class="fa fa-question fa-fw mr-2 fa-lg"></i>FAQ</a>
<a class="dropdown-item" href="../doc/tutorials/"><i class="fa fa-book fa-fw mr-2 fa-lg"></i>Tutorials</a>
<a class="dropdown-item" href="/workshop/"><i class="fa fa-graduation-cap fa-fw mr-2 fa-lg"></i>Workshop</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="https://stackoverflow.com/questions/tagged/openlayers"><i class="fa fa-stack-overflow fa-fw mr-2"></i>Ask a Question</a>
</div>
</li>
<li class="nav-item active"><a class="nav-link" href="../examples/">Examples</a></li>
<li class="nav-item"><a class="nav-link" href="../apidoc/"><i class="fa fa-sitemap mr-1"></i>API</a></li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="codedropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Code</a>
<div class="dropdown-menu dropdown-menu-right mb-3" aria-labelledby="codedropdown">
<a class="dropdown-item" href="https://github.com/openlayers/openlayers"><i class="fa fa-github fa-fw mr-2 fa-lg"></i>Repository</a>
<a class="dropdown-item" href="/download/"><i class="fa fa-download fa-fw mr-2 fa-lg"></i>Download</a>
</div>
</li>
</ul>
</nav>
</header>
<div class="container-fluid line-numbers">
<div id="latest-check" class="alert alert-warning alert-dismissible" role="alert" style="display:none">
<button id="latest-dismiss" type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
This example uses OpenLayers v<span>6.5.0</span>. The <a id="latest-link" href="#" class="alert-link">latest</a> is v<span id="latest-version"></span>.
</div>
<div class="row-fluid">
<a href="#" id="codepen-button" class="btn btn-link float-right">
<i class="fa fa-codepen fa-lg"></i> Edit
</a>
<div class="span12">
<h4 id="title">Tracing around a polygon</h4>
<p class="tags">
<span class="badge-group">
<a
href="./index.html?q=draw" class="badge badge-info">draw</a
><a
class="badge badge-info tag-modal-toggle text-white"
data-toggle="modal"
data-target="#tag-example-list"
data-title="draw"
data-content="
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./draw-and-modify-features.html&quot;&gt;Draw and Modify Features&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./draw-features.html&quot;&gt;Draw Features&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./draw-shapes.html&quot;&gt;Draw Shapes&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./draw-freehand.html&quot;&gt;Freehand Drawing&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./line-arrows.html&quot;&gt;LineString Arrows&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./measure.html&quot;&gt;Measure&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./snap.html&quot;&gt;Snap Interaction&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./topolis.html&quot;&gt;topolis integration&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action active&quot; href=&quot;./tracing.html&quot;&gt;Tracing around a polygon&lt;/a&gt;"
tabindex="0"
>9</a>
</span>
<span class="badge-group">
<a
href="./index.html?q=trace" class="badge badge-info">trace</a
><a
class="badge badge-info tag-modal-toggle text-white"
data-toggle="modal"
data-target="#tag-example-list"
data-title="trace"
data-content="
&lt;a class=&quot;list-group-item list-group-item-action active&quot; href=&quot;./tracing.html&quot;&gt;Tracing around a polygon&lt;/a&gt;"
tabindex="0"
>1</a>
</span>
<span class="badge-group">
<a
href="./index.html?q=snap" class="badge badge-info">snap</a
><a
class="badge badge-info tag-modal-toggle text-white"
data-toggle="modal"
data-target="#tag-example-list"
data-title="snap"
data-content="
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./snap.html&quot;&gt;Snap Interaction&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action active&quot; href=&quot;./tracing.html&quot;&gt;Tracing around a polygon&lt;/a&gt;"
tabindex="0"
>2</a>
</span>
<span class="badge-group">
<a
href="./index.html?q=vector" class="badge badge-info">vector</a
><a
class="badge badge-info tag-modal-toggle text-white"
data-toggle="modal"
data-target="#tag-example-list"
data-title="vector"
data-content="
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./mapbox-vector-tiles-advanced.html&quot;&gt;Advanced Mapbox Vector Tiles&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./animated-gif.html&quot;&gt;Animated GIF&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./vector-esri.html&quot;&gt;ArcGIS REST Feature Service&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./cluster.html&quot;&gt;Clustered Features&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./feature-animation.html&quot;&gt;Custom Animation&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./custom-circle-render.html&quot;&gt;Custom Circle Render&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./custom-interactions.html&quot;&gt;Custom Interactions&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./polygon-styles.html&quot;&gt;Custom Polygon Styles&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./drag-and-drop-image-vector.html&quot;&gt;Drag-and-Drop Image Vector&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./draw-and-modify-features.html&quot;&gt;Draw and Modify Features&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./draw-features.html&quot;&gt;Draw Features&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./draw-shapes.html&quot;&gt;Draw Shapes&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./earthquake-clusters.html&quot;&gt;Earthquake Clusters&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./heatmap-earthquakes.html&quot;&gt;Earthquakes Heatmap&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./kml-earthquakes.html&quot;&gt;Earthquakes in KML&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./earthquake-custom-symbol.html&quot;&gt;Earthquakes with custom symbols&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./vector-esri-edit.html&quot;&gt;Editable ArcGIS REST Feature Service&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./flight-animation.html&quot;&gt;Flight Animation&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./fractal.html&quot;&gt;Fractal Rendering&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./draw-freehand.html&quot;&gt;Freehand Drawing&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./geojson.html&quot;&gt;GeoJSON&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./geojson-vt.html&quot;&gt;geojson-vt integration&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./icon-color.html&quot;&gt;Icon Colors&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./modify-icon.html&quot;&gt;Icon modification&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./icon-negative.html&quot;&gt;Icon Pixel Operations&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./icon-scale.html&quot;&gt;Icon Scale&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./icon.html&quot;&gt;Icon Symbolizer&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./jsts.html&quot;&gt;JSTS Integration&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./line-arrows.html&quot;&gt;LineString Arrows&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./mapbox-vector-layer.html&quot;&gt;Mapbox Vector Layer&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./mapbox-vector-tiles.html&quot;&gt;Mapbox Vector Tiles&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./mapbox-layer.html&quot;&gt;Mapbox-gl Layer&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./measure.html&quot;&gt;Measure&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./modify-features.html&quot;&gt;Modify Features&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./modify-test.html&quot;&gt;Modify Features Test&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./osm-vector-tiles.html&quot;&gt;OSM Vector Tiles&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./vector-osm.html&quot;&gt;OSM XML&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./regularshape.html&quot;&gt;Regular Shapes&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./select-features.html&quot;&gt;Select Features&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./select-hover-features.html&quot;&gt;Select Features by Hover&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./select-multiple-features.html&quot;&gt;Select multiple Features&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./snap.html&quot;&gt;Snap Interaction&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./street-labels.html&quot;&gt;Street Labels&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./style-renderer.html&quot;&gt;Style renderer&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./synthetic-lines.html&quot;&gt;Synthetic Lines&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./synthetic-points.html&quot;&gt;Synthetic Points&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./kml-timezones.html&quot;&gt;Timezones in KML&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./topojson.html&quot;&gt;TopoJSON&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./topolis.html&quot;&gt;topolis integration&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action active&quot; href=&quot;./tracing.html&quot;&gt;Tracing around a polygon&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./translate-features.html&quot;&gt;Translate Features&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./turf.html&quot;&gt;turf.js&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./layer-clipping-vector.html&quot;&gt;Vector Clipping Layer&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./image-vector-layer.html&quot;&gt;Vector Image Layer&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./vector-label-decluttering.html&quot;&gt;Vector Label Decluttering&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./vector-labels.html&quot;&gt;Vector Labels&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./vector-layer.html&quot;&gt;Vector Layer&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./hitdetect-vector.html&quot;&gt;Vector Layer Hit Detection&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./vector-wfs.html&quot;&gt;WFS&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./vector-wfs-getfeature.html&quot;&gt;WFS - GetFeature&lt;/a&gt;"
tabindex="0"
>60</a>
</span>
<span class="badge-group">
<a
href="./index.html?q=topology" class="badge badge-info">topology</a
><a
class="badge badge-info tag-modal-toggle text-white"
data-toggle="modal"
data-target="#tag-example-list"
data-title="topology"
data-content="
&lt;a class=&quot;list-group-item list-group-item-action&quot; href=&quot;./topolis.html&quot;&gt;topolis integration&lt;/a&gt;
&lt;a class=&quot;list-group-item list-group-item-action active&quot; href=&quot;./tracing.html&quot;&gt;Tracing around a polygon&lt;/a&gt;"
tabindex="0"
>2</a>
</span>
</p>
<div class="modal modal-tag-example" id="tag-example-list" tabindex="-1" role="dialog" aria-labelledby="tag-example-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="tag-example-title"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="list-group"></div>
</div>
</div>
</div>
</div>
<div id="map" class="map"></div>
<form class="form-inline">
<label for="type">Geometry type &nbsp;</label>
<select id="type">
<option value="Polygon">Polygon</option>
<option value="LineString">LineString</option>
<option value="None">None</option>
</select>
</form>
</div>
<form method="POST" id="codepen-form" target="_blank" action="https://codesandbox.io/api/v1/sandboxes/define">
<input id="codesandbox-params" type="hidden" name="parameters">
</form>
</div>
<div class="row-fluid">
<div class="span12">
<p id="shortdesc">Example of setting up a draw interaction to easily snap to an existing feature.</p>
<div id="docs"><p>This example showcases how the draw interaction API can be set up to make snapping along an existing geometry easier while preserving topology, which is sometimes called &quot;tracing&quot;. When the user clicks on two different points on the Idaho state border, the part of the border comprised between these two points is added to the currently drawn feature. This leverages the <code>appendCoordinates</code> method of the <code>ol/interaction/Draw</code> interaction.</p>
</div>
</div>
</div>
<div class="row-fluid">
<h5 class="source-heading">main.js</h5>
<pre><code id="example-js-source" class="language-js">import 'ol/ol.css';
import Draw from &#x27;ol/interaction/Draw&#x27;;
import Feature from &#x27;ol/Feature&#x27;;
import Fill from &#x27;ol/style/Fill&#x27;;
import GeoJSON from &#x27;ol/format/GeoJSON&#x27;;
import LineString from &#x27;ol/geom/LineString&#x27;;
import Map from &#x27;ol/Map&#x27;;
import Snap from &#x27;ol/interaction/Snap&#x27;;
import Stroke from &#x27;ol/style/Stroke&#x27;;
import Style from &#x27;ol/style/Style&#x27;;
import View from &#x27;ol/View&#x27;;
import {OSM, Vector as VectorSource} from &#x27;ol/source&#x27;;
import {Tile as TileLayer, Vector as VectorLayer} from &#x27;ol/layer&#x27;;
// math utilities
// coordinates; will return the length of the [a, b] segment
function length(a, b) {
return Math.sqrt(
(b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])
);
}
// coordinates; will return true if c is on the [a, b] segment
function isOnSegment(c, a, b) {
var lengthAc &#x3D; length(a, c);
var lengthAb &#x3D; length(a, b);
var dot &#x3D;
((c[0] - a[0]) * (b[0] - a[0]) + (c[1] - a[1]) * (b[1] - a[1])) / lengthAb;
return Math.abs(lengthAc - dot) &lt; 1e-6 &amp;&amp; lengthAc &lt; lengthAb;
}
// modulo for negative values, eg: mod(-1, 4) returns 3
function mod(a, b) {
return ((a % b) + b) % b;
}
// returns a coordinates array which contains the segments of the feature&#x27;s
// outer ring between the start and end points
// Note: this assumes the base feature is a single polygon
function getPartialRingCoords(feature, startPoint, endPoint) {
var polygon &#x3D; feature.getGeometry();
if (polygon.getType() &#x3D;&#x3D;&#x3D; &#x27;MultiPolygon&#x27;) {
polygon &#x3D; polygon.getPolygon(0);
}
var ringCoords &#x3D; polygon.getLinearRing().getCoordinates();
var i,
pointA,
pointB,
startSegmentIndex &#x3D; -1;
for (i &#x3D; 0; i &lt; ringCoords.length; i++) {
pointA &#x3D; ringCoords[i];
pointB &#x3D; ringCoords[mod(i + 1, ringCoords.length)];
// check if this is the start segment dot product
if (isOnSegment(startPoint, pointA, pointB)) {
startSegmentIndex &#x3D; i;
break;
}
}
var cwCoordinates &#x3D; [];
var cwLength &#x3D; 0;
var ccwCoordinates &#x3D; [];
var ccwLength &#x3D; 0;
// build clockwise coordinates
for (i &#x3D; 0; i &lt; ringCoords.length; i++) {
pointA &#x3D;
i &#x3D;&#x3D;&#x3D; 0
? startPoint
: ringCoords[mod(i + startSegmentIndex, ringCoords.length)];
pointB &#x3D; ringCoords[mod(i + startSegmentIndex + 1, ringCoords.length)];
cwCoordinates.push(pointA);
if (isOnSegment(endPoint, pointA, pointB)) {
cwCoordinates.push(endPoint);
cwLength +&#x3D; length(pointA, endPoint);
break;
} else {
cwLength +&#x3D; length(pointA, pointB);
}
}
// build counter-clockwise coordinates
for (i &#x3D; 0; i &lt; ringCoords.length; i++) {
pointA &#x3D; ringCoords[mod(startSegmentIndex - i, ringCoords.length)];
pointB &#x3D;
i &#x3D;&#x3D;&#x3D; 0
? startPoint
: ringCoords[mod(startSegmentIndex - i + 1, ringCoords.length)];
ccwCoordinates.push(pointB);
if (isOnSegment(endPoint, pointA, pointB)) {
ccwCoordinates.push(endPoint);
ccwLength +&#x3D; length(endPoint, pointB);
break;
} else {
ccwLength +&#x3D; length(pointA, pointB);
}
}
// keep the shortest path
return ccwLength &lt; cwLength ? ccwCoordinates : cwCoordinates;
}
// layers definition
var raster &#x3D; new TileLayer({
source: new OSM(),
});
// features in this layer will be snapped to
var baseVector &#x3D; new VectorLayer({
source: new VectorSource({
format: new GeoJSON(),
url:
&quot;https://ahocevar.com/geoserver/wfs?service&#x3D;wfs&amp;request&#x3D;getfeature&amp;typename&#x3D;topp:states&amp;cql_filter&#x3D;STATE_NAME&#x3D;&#x27;Idaho&#x27;&amp;outputformat&#x3D;application/json&quot;,
}),
});
// this is were the drawn features go
var drawVector &#x3D; new VectorLayer({
source: new VectorSource(),
style: new Style({
stroke: new Stroke({
color: &#x27;rgba(100, 255, 0, 1)&#x27;,
width: 2,
}),
fill: new Fill({
color: &#x27;rgba(100, 255, 0, 0.3)&#x27;,
}),
}),
});
// this line only appears when we&#x27;re tracing a feature outer ring
var previewLine &#x3D; new Feature({
geometry: new LineString([]),
});
var previewVector &#x3D; new VectorLayer({
source: new VectorSource({
features: [previewLine],
}),
style: new Style({
stroke: new Stroke({
color: &#x27;rgba(255, 0, 0, 1)&#x27;,
width: 2,
}),
}),
});
var map &#x3D; new Map({
layers: [raster, baseVector, drawVector, previewVector],
target: &#x27;map&#x27;,
view: new View({
center: [-12986427, 5678422],
zoom: 5,
}),
});
var drawInteraction, tracingFeature, startPoint, endPoint;
var drawing &#x3D; false;
var getFeatureOptions &#x3D; {
hitTolerance: 10,
layerFilter: function (layer) {
return layer &#x3D;&#x3D;&#x3D; baseVector;
},
};
// the click event is used to start/end tracing around a feature
map.on(&#x27;click&#x27;, function (event) {
if (!drawing) {
return;
}
var hit &#x3D; false;
map.forEachFeatureAtPixel(
event.pixel,
function (feature) {
if (tracingFeature &amp;&amp; feature !&#x3D;&#x3D; tracingFeature) {
return;
}
hit &#x3D; true;
var coord &#x3D; map.getCoordinateFromPixel(event.pixel);
// second click on the tracing feature: append the ring coordinates
if (feature &#x3D;&#x3D;&#x3D; tracingFeature) {
endPoint &#x3D; tracingFeature.getGeometry().getClosestPoint(coord);
var appendCoords &#x3D; getPartialRingCoords(
tracingFeature,
startPoint,
endPoint
);
drawInteraction.removeLastPoint();
drawInteraction.appendCoordinates(appendCoords);
tracingFeature &#x3D; null;
}
// start tracing on the feature ring
tracingFeature &#x3D; feature;
startPoint &#x3D; tracingFeature.getGeometry().getClosestPoint(coord);
},
getFeatureOptions
);
if (!hit) {
// clear current tracing feature &amp; preview
previewLine.getGeometry().setCoordinates([]);
tracingFeature &#x3D; null;
}
});
// the pointermove event is used to show a preview of the result of the tracing
map.on(&#x27;pointermove&#x27;, function (event) {
if (tracingFeature &amp;&amp; drawing) {
var coord &#x3D; null;
map.forEachFeatureAtPixel(
event.pixel,
function (feature) {
if (tracingFeature &#x3D;&#x3D;&#x3D; feature) {
coord &#x3D; map.getCoordinateFromPixel(event.pixel);
}
},
getFeatureOptions
);
var previewCoords &#x3D; [];
if (coord) {
endPoint &#x3D; tracingFeature.getGeometry().getClosestPoint(coord);
previewCoords &#x3D; getPartialRingCoords(
tracingFeature,
startPoint,
endPoint
);
}
previewLine.getGeometry().setCoordinates(previewCoords);
}
});
var snapInteraction &#x3D; new Snap({
source: baseVector.getSource(),
});
var typeSelect &#x3D; document.getElementById(&#x27;type&#x27;);
function addInteraction() {
var value &#x3D; typeSelect.value;
if (value !&#x3D;&#x3D; &#x27;None&#x27;) {
drawInteraction &#x3D; new Draw({
source: drawVector.getSource(),
type: typeSelect.value,
});
drawInteraction.on(&#x27;drawstart&#x27;, function () {
drawing &#x3D; true;
});
drawInteraction.on(&#x27;drawend&#x27;, function () {
drawing &#x3D; false;
previewLine.getGeometry().setCoordinates([]);
tracingFeature &#x3D; null;
});
map.addInteraction(drawInteraction);
map.addInteraction(snapInteraction);
}
}
typeSelect.onchange &#x3D; function () {
map.removeInteraction(drawInteraction);
map.removeInteraction(snapInteraction);
addInteraction();
};
addInteraction();
</code></pre>
</div>
<div class="row-fluid">
<h5 class="source-heading">index.html</h5>
<pre><code id="example-html-source" class="language-markup">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;title&gt;Tracing around a polygon&lt;/title&gt;
&lt;!-- Pointer events polyfill for old browsers, see https://caniuse.com/#feat=pointer --&gt;
&lt;script src="https://unpkg.com/elm-pep"&gt;&lt;/script&gt;
&lt;style&gt;
.map {
width: 100%;
height:400px;
}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id&#x3D;&quot;map&quot; class&#x3D;&quot;map&quot;&gt;&lt;/div&gt;
&lt;form class&#x3D;&quot;form-inline&quot;&gt;
&lt;label for&#x3D;&quot;type&quot;&gt;Geometry type &amp;nbsp;&lt;/label&gt;
&lt;select id&#x3D;&quot;type&quot;&gt;
&lt;option value&#x3D;&quot;Polygon&quot;&gt;Polygon&lt;/option&gt;
&lt;option value&#x3D;&quot;LineString&quot;&gt;LineString&lt;/option&gt;
&lt;option value&#x3D;&quot;None&quot;&gt;None&lt;/option&gt;
&lt;/select&gt;
&lt;/form&gt;
&lt;script src="main.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</div>
<div class="row-fluid">
<h5 class="source-heading">package.json</h5>
<pre><code id="example-pkg-source" class="language-json">{
&quot;name&quot;: &quot;tracing&quot;,
&quot;dependencies&quot;: {
&quot;ol&quot;: &quot;6.5.0&quot;
},
&quot;devDependencies&quot;: {
&quot;parcel&quot;: &quot;^2.0.0-beta.1&quot;
},
&quot;scripts&quot;: {
&quot;start&quot;: &quot;parcel index.html&quot;,
&quot;build&quot;: &quot;parcel build --public-url . index.html&quot;
}
}</code></pre>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js"></script>
<script src="./resources/prism/prism-1.20.0.min.js"></script>
<script src="./resources/common.js"></script>
<script src="common.js"></script>
<script src="tracing.js"></script>
<script>
$('#tag-example-list').on('show.bs.modal', function (event) {
const button = $(event.relatedTarget); // Button that triggered the modal
const title = button.data('title');
const content = button.data('content');
const modal = $(this)
modal.find('.modal-title').text(title);
modal.find('.modal-body').html(content);
});
var packageUrl = 'https://raw.githubusercontent.com/openlayers/openlayers.github.io/build/package.json';
fetch(packageUrl).then(function(response) {
return response.json();
}).then(function(json) {
var latestVersion = json.version;
document.getElementById('latest-version').innerHTML = latestVersion;
var url = window.location.href;
var branchSearch = url.match(/\/([^\/]*)\/examples\//);
var cookieText = 'dismissed=-' + latestVersion + '-';
var dismissed = document.cookie.indexOf(cookieText) != -1;
if (branchSearch && !dismissed && /^v[0-9\.]*$/.test(branchSearch[1]) && '6.5.0' != latestVersion) {
var link = url.replace(branchSearch[0], '/latest/examples/');
fetch(link, {method: 'head'}).then(function(response) {
var a = document.getElementById('latest-link');
a.href = response.status == 200 ? link : '../../latest/examples/';
});
var latestCheck = document.getElementById('latest-check');
latestCheck.style.display = '';
document.getElementById('latest-dismiss').onclick = function() {
latestCheck.style.display = 'none';
document.cookie = cookieText;
}
}
});
</script>
</body>
</html>