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.

428 lines
26 KiB
HTML

2 weeks ago
<!DOCTYPE html>
<html>
<head>
<title>OpenLayers - Frequently Asked Questions (FAQ)</title>
<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>
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>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<link href='https://fonts.googleapis.com/css?family=Quattrocento+Sans:400,400italic,700' rel='stylesheet' type='text/css'>
<script src="//code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/components/prism-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/autoloader/prism-autoloader.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/toolbar/prism-toolbar.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.4/clipboard.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.20.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link href='../../../assets/theme/site.css' rel='stylesheet' type='text/css'>
<link rel="icon" type="image/x-icon" href="../../../assets/theme/img/favicon.ico" />
</head>
<body>
<header class="navbar navbar-expand-md navbar-dark mb-3 py-0 fixed-top" role="navigation">
<a href='/' class='navbar-brand'><img src='../../../assets/theme/img/logo70.png'>&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 active" 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="/en/latest/doc/">Docs</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="/en/latest/doc/quickstart.html"><i class="fa fa-check fa-fw mr-2 fa-lg"></i>Quick Start</a>
<a class="dropdown-item" href="/en/latest/doc/faq.html"><i class="fa fa-question fa-fw mr-2 fa-lg"></i>FAQ</a>
<a class="dropdown-item" href="/en/latest/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"><a class="nav-link" href="/en/latest/examples/">Examples</a></li>
<li class="nav-item"><a class="nav-link" href="/en/latest/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'>
<h1 id="frequently-asked-questions-faq-">Frequently Asked Questions (FAQ)</h1>
<p>Certain questions arise more often than others when users ask for help. This
document tries to list some of the common questions that frequently get asked,
e.g. on <a href="https://stackoverflow.com/questions/tagged/openlayers">Stack Overflow</a>.</p>
<p>If you think a question (and naturally its answer) should be added here, feel
free to ping us or to send a pull request enhancing this document.</p>
<p>Table of contents:</p>
<ul>
<li><a href="#what-projection-is-openlayers-using-">What projection is OpenLayers using?</a></li>
<li><a href="#how-do-i-change-the-projection-of-my-map-">How do I change the projection of my map?</a></li>
<li><a href="#why-is-my-map-centered-on-the-gulf-of-guinea-or-africa-the-ocean-null-island-">Why is my map centered on the gulf of guinea (or africa, the ocean, null-island)?</a></li>
<li><a href="#why-is-the-order-of-a-coordinate-lon-lat-and-not-lat-lon-">Why is the order of a coordinate [lon,lat], and not [lat,lon]?</a></li>
<li><a href="#why-aren-t-there-any-features-in-my-source-">Why aren&#39;t there any features in my source?</a></li>
<li><a href="#how-do-i-force-a-re-render-of-the-map-">How do I force a re-render of the map?</a></li>
<li><a href="#why-are-my-features-not-found-">Why are my features not found?</a></li>
<li><a href="#user-content-why-is-zooming-or-clicking-off-inaccurate">Why is zooming or clicking off, inaccurate?</a></li>
</ul>
<h2 id="what-projection-is-openlayers-using-">What projection is OpenLayers using?</h2>
<p>Every map that you&#39;ll create with OpenLayers will have a view, and every view
will have a projection. As the earth is three-dimensional and round but the 2D
view of a map isn&#39;t, we need a mathematical expression to represent it. Enter
projections.</p>
<p>There isn&#39;t only one projection, but there are many common ones. Each projection
has different properties, in that it accurately represents distances, angles or
areas. Certain projections are better suited for different regions in the world.</p>
<p>Back to the original question: OpenLayers is capable of dealing with most
projections. If you do not explicitly set one, your map is going to use our
default which is the Web Mercator projection (EPSG:3857). The same projection is
used e.g. for the maps of the OpenStreetMap-project and commercial products such
as Bing Maps or Google Maps.</p>
<p>This projection is a good choice if you want a map which shows the whole world,
and you may need to have this projection if you want to e.g. use the
OpenStreetMap or Bing tiles.</p>
<h2 id="how-do-i-change-the-projection-of-my-map-">How do I change the projection of my map?</h2>
<p>There is a good chance that you want to change the default projection of
OpenLayers to something more appropriate for your region or your specific data.</p>
<p>The projection of your map can be set through the <code>view</code>-property. Here are some
examples:</p>
<pre><code class="language-javascript">import Map from <span class="string">'ol/Map'</span>;
import View from <span class="string">'ol/View'</span>;
<span class="comment">// OpenLayers comes with support for the World Geodetic System 1984, EPSG:4326:</span>
<span class="keyword">const</span> map = <span class="keyword">new</span> Map({
view: <span class="keyword">new</span> View({
projection: <span class="string">'EPSG:4326'</span>
<span class="comment">// other view properties like map center etc.</span>
})
<span class="comment">// other properties for your map like layers etc.</span>
});</code></pre>
<pre><code class="language-javascript">import Map from <span class="string">'ol/Map'</span>;
import View from <span class="string">'ol/View'</span>;
import proj4 from <span class="string">'proj4'</span>;
import {register} from <span class="string">'ol/proj/proj4'</span>;
import {get as getProjection} from <span class="string">'ol/proj'</span>;
<span class="comment">// To use other projections, you have to register the projection in OpenLayers.</span>
<span class="comment">// This can easily be done with [http://proj4js.org/](proj4)</span>
<span class="comment">//</span>
<span class="comment">// By default OpenLayers does not know about the EPSG:21781 (Swiss) projection.</span>
<span class="comment">// So we create a projection instance for EPSG:21781 and pass it to</span>
<span class="comment">// register to make it available to the library for lookup by its</span>
<span class="comment">// code.</span>
proj4.defs(<span class="string">'EPSG:21781'</span>,
<span class="string">'+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 '</span> +
<span class="string">'+x_0=600000 +y_0=200000 +ellps=bessel '</span> +
<span class="string">'+towgs84=660.077,13.551,369.344,2.484,1.783,2.939,5.66 +units=m +no_defs'</span>);
register(proj4);
<span class="keyword">const</span> swissProjection = getProjection(<span class="string">'EPSG:21781'</span>);
<span class="comment">// we can now use the projection:</span>
<span class="keyword">const</span> map = <span class="keyword">new</span> Map({
view: <span class="keyword">new</span> View({
projection: swissProjection
<span class="comment">// other view properties like map center etc.</span>
})
<span class="comment">// other properties for your map like layers etc.</span>
});</code></pre>
<p>We recommend to lookup parameters of your projection (like the validity extent)
over at <a href="https://epsg.io/">epsg.io</a>.</p>
<h2 id="why-is-my-map-centered-on-the-gulf-of-guinea-or-africa-the-ocean-null-island-">Why is my map centered on the gulf of guinea (or africa, the ocean, null-island)?</h2>
<p>If you have set a center in your map view, but don&#39;t see a real change in visual
output, chances are that you have provided the coordinates of the map center in
the wrong (a non-matching) projection.</p>
<p>As the default projection in OpenLayers is Web Mercator (see above), the
coordinates for the center have to be provided in that projection. Chances are
that your map looks like this:</p>
<pre><code class="language-javascript">import Map from <span class="string">'ol/Map'</span>;
import View from <span class="string">'ol/View'</span>;
import TileLayer from <span class="string">'ol/layer/Tile'</span>;
import OSM from <span class="string">'ol/source/OSM'</span>;
<span class="keyword">const</span> washingtonLonLat = [-<span class="number">77.036667</span>, <span class="number">38.895</span>];
<span class="keyword">const</span> map = <span class="keyword">new</span> Map({
layers: [
<span class="keyword">new</span> TileLayer({
source: <span class="keyword">new</span> OSM()
})
],
target: <span class="string">'map'</span>,
view: <span class="keyword">new</span> View({
center: washingtonLonLat,
zoom: <span class="number">12</span>
})
});</code></pre>
<p>Here <code>[-77.036667, 38.895]</code> is provided as the center of the view. But as Web
Mercator is a metric projection, you are currently telling OpenLayers that the
center shall be some meters (~77m and ~39m respectively) away from <code>[0, 0]</code>. In
the Web Mercator projection the coordinate is right in the gulf of guinea.</p>
<p>The solution is easy: Provide the coordinates projected into Web Mercator.
OpenLayers has some helpful utility methods to assist you:</p>
<pre><code class="language-javascript">import Map from <span class="string">'ol/Map'</span>;
import View from <span class="string">'ol/View'</span>;
import TileLayer from <span class="string">'ol/layer/Tile'</span>;
import OSM from <span class="string">'ol/source/OSM'</span>;
import {fromLonLat} from <span class="string">'ol/proj'</span>;
<span class="keyword">const</span> washingtonLonLat = [-<span class="number">77.036667</span>, <span class="number">38.895</span>];
<span class="keyword">const</span> washingtonWebMercator = fromLonLat(washingtonLonLat);
<span class="keyword">const</span> map = <span class="keyword">new</span> Map({
layers: [
<span class="keyword">new</span> TileLayer({
source: <span class="keyword">new</span> OSM()
})
],
target: <span class="string">'map'</span>,
view: <span class="keyword">new</span> View({
center: washingtonWebMercator,
zoom: <span class="number">8</span>
})
});</code></pre>
<p>The method <code>fromLonLat()</code> is available from version 3.5 onwards.</p>
<p>If you told OpenLayers about a custom projection (see above), you can use the
following method to transform a coordinate from WGS84 to your projection:</p>
<pre><code class="language-javascript">import {transform} from <span class="string">'ol/proj'</span>;
<span class="comment">// assuming that OpenLayers knows about EPSG:21781, see above</span>
<span class="keyword">const</span> swissCoord = transform([<span class="number">8.23</span>, <span class="number">46.86</span>], <span class="string">'EPSG:4326'</span>, <span class="string">'EPSG:21781'</span>);</code></pre>
<h2 id="why-is-the-order-of-a-coordinate-lon-lat-and-not-lat-lon-">Why is the order of a coordinate [lon,lat], and not [lat,lon]?</h2>
<p>Because of two different and incompatible conventions. Latitude and longitude
are normally given in that order. Maps are 2D representations/projections
of the earth&#39;s surface, with coordinates expressed in the <code>x,y</code> grid of the
<a href="https://en.wikipedia.org/wiki/Cartesian_coordinate_system">Cartesian system</a>.
As they are by convention drawn with west on the left and north at the top,
this means that <code>x</code> represents longitude, and <code>y</code> latitude. As stated above,
OpenLayers is designed to handle all projections, but the default view is in
projected Cartesian coordinates. It would make no sense to have duplicate
functions to handle coordinates in both the Cartesian <code>x,y</code> and <code>lat,lon</code>
systems, so the degrees of latitude and longitude should be entered as though
they were Cartesian, in other words, they are <code>lon,lat</code>.</p>
<p>If you have difficulty remembering which way round it is, use the language code
for English, <code>en</code>, as a mnemonic: East before North.</p>
<h4 id="a-practical-example">A practical example</h4>
<p>So you want to center your map on a certain place on the earth and obviously you
need to have its coordinates for this. Let&#39;s assume you want your map centered
on Schladming, a beautiful place in Austria. Head over to the wikipedia
page for <a href="https://en.wikipedia.org/wiki/Schladming">Schladming</a>. In the top-right
corner there is a link to <a href="https://geohack.toolforge.org/geohack.php?pagename=Schladming&amp;params=47_23_39_N_13_41_21_E_type:city(4565">GeoHack</a>_region:AT-6),
which effectively tells you the coordinates are:</p>
<pre><code>WGS84:
47° 23 39″ N, 13° 41 21″ E
47.394167, 13.689167</code></pre>
<p>So the next step would be to put the decimal coordinates into an array and use
it as center:</p>
<pre><code class="language-javascript">import Map from <span class="string">'ol/Map'</span>;
import View from <span class="string">'ol/View'</span>;
import TileLayer from <span class="string">'ol/layer/Tile'</span>;
import OSM from <span class="string">'ol/source/OSM'</span>;
import {fromLonLat} from <span class="string">'ol/proj'</span>;
<span class="keyword">const</span> schladming = [<span class="number">47.394167</span>, <span class="number">13.689167</span>]; <span class="comment">// caution partner, read on...</span>
<span class="comment">// since we are using OSM, we have to transform the coordinates...</span>
<span class="keyword">const</span> schladmingWebMercator = fromLonLat(schladming);
<span class="keyword">const</span> map = <span class="keyword">new</span> Map({
layers: [
<span class="keyword">new</span> TileLayer({
source: <span class="keyword">new</span> OSM()
})
],
target: <span class="string">'map'</span>,
view: <span class="keyword">new</span> View({
center: schladmingWebMercator,
zoom: <span class="number">9</span>
})
});</code></pre>
<p>Running the above example will possibly surprise you, since we are not centered
on Schladming, Austria, but instead on Abyan, a region in Yemen (possibly also a
nice place). So what happened?</p>
<p>Many people mix up the order of longitude and latitude in a coordinate array.
Don&#39;t worry if you get it wrong at first, many OpenLayers developers have to
think twice about whether to put the longitude or the latitude first when they
e.g. try to change the map center.</p>
<p>Ok, then let&#39;s flip the coordinates:</p>
<pre><code class="language-javascript">import Map from <span class="string">'ol/Map'</span>;
import View from <span class="string">'ol/View'</span>;
import TileLayer from <span class="string">'ol/layer/Tile'</span>;
import OSM from <span class="string">'ol/source/OSM'</span>;
import {fromLonLat} from <span class="string">'ol/proj'</span>;
<span class="keyword">const</span> schladming = [<span class="number">13.689167</span>, <span class="number">47.394167</span>]; <span class="comment">// longitude first, then latitude</span>
<span class="comment">// since we are using OSM, we have to transform the coordinates...</span>
<span class="keyword">const</span> schladmingWebMercator = fromLonLat(schladming);
<span class="keyword">const</span> map = <span class="keyword">new</span> Map({
layers: [
<span class="keyword">new</span> TileLayer({
source: <span class="keyword">new</span> OSM()
})
],
target: <span class="string">'map'</span>,
view: <span class="keyword">new</span> View({
center: schladmingWebMercator,
zoom: <span class="number">9</span>
})
});</code></pre>
<p>Schladming is now correctly displayed in the center of the map.</p>
<p>So when you deal with EPSG:4326 coordinates in OpenLayers, put the longitude
first, and then the latitude. This behaviour is the same as we had in OpenLayers
2, and it actually makes sense because of the natural axis order in WGS84.</p>
<p>If you cannot remember the correct order, just have a look at the method name
we used: <code>fromLonLat</code>; even there we hint that we expect longitude
first, and then latitude.</p>
<h2 id="why-aren-t-there-any-features-in-my-source-">Why aren&#39;t there any features in my source?</h2>
<p>Suppose you want to load a KML file and display the contained features on the
map. Code like the following could be used:</p>
<pre><code class="language-javascript">import VectorLayer from <span class="string">'ol/layer/Vector'</span>;
import KMLSource from <span class="string">'ol/source/KML'</span>;
<span class="keyword">const</span> vector = <span class="keyword">new</span> VectorLayer({
source: <span class="keyword">new</span> KMLSource({
projection: <span class="string">'EPSG:3857'</span>,
url: <span class="string">'data/kml/2012-02-10.kml'</span>
})
});</code></pre>
<p>You may ask yourself how many features are in that KML, and try something like
the following:</p>
<pre><code class="language-javascript">import VectorLayer from <span class="string">'ol/layer/Vector'</span>;
import KMLSource from <span class="string">'ol/source/KML'</span>;
<span class="keyword">const</span> vector = <span class="keyword">new</span> VectorLayer({
source: <span class="keyword">new</span> KMLSource({
projection: <span class="string">'EPSG:3857'</span>,
url: <span class="string">'data/kml/2012-02-10.kml'</span>
})
});
<span class="keyword">const</span> numFeatures = vector.getSource().getFeatures().length;
console.log(<span class="string">"Count right after construction: "</span> + numFeatures);</code></pre>
<p>This will log a count of <code>0</code> features to be in the source. This is because the
loading of the KML-file will happen in an asynchronous manner. To get the count
as soon as possible (right after the file has been fetched and the source has
been populated with features), you should use an event listener function on the
<code>source</code>:</p>
<pre><code class="language-javascript">vector.getSource().on(<span class="string">'change'</span>, <span class="function"><span class="keyword">function</span><span class="params">(evt)</span>{</span>
<span class="keyword">const</span> source = evt.target;
<span class="keyword">if</span> (source.getState() === <span class="string">'ready'</span>) {
<span class="keyword">const</span> numFeatures = source.getFeatures().length;
console.log(<span class="string">"Count after change: "</span> + numFeatures);
}
});</code></pre>
<p>This will correctly report the number of features, <code>1119</code> in that particular
case.</p>
<h2 id="how-do-i-force-a-re-render-of-the-map-">How do I force a re-render of the map?</h2>
<p>Usually the map is automatically re-rendered, once a source changes (for example
when a remote source has loaded).</p>
<p>If you actually want to manually trigger a rendering, you could use</p>
<pre><code class="language-javascript">map.render();</code></pre>
<p>...or its companion method</p>
<pre><code class="language-javascript">map.renderSync();</code></pre>
<h2 id="why-are-my-features-not-found-">Why are my features not found?</h2>
<p>You are using <code>Map#forEachFeatureAtPixel</code> or <code>Map#hasFeatureAtPixel</code>, but
it sometimes does not work for large icons or labels? The <em>hit detection</em> only
checks features that are within a certain distance of the given position. For large
icons, the actual geometry of a feature might be too far away and is not considered.</p>
<p>In this case, set the <code>renderBuffer</code> property of <code>VectorLayer</code> (the default value is 100px):</p>
<pre><code class="language-javascript">import VectorLayer from <span class="string">'ol/layer/Vector'</span>;
<span class="keyword">const</span> vectorLayer = <span class="keyword">new</span> VectorLayer({
...
renderBuffer: <span class="number">200</span>
});</code></pre>
<p>The recommended value is the size of the largest symbol, line width or label.</p>
<h2 id="why-is-zooming-or-clicking-in-the-map-off-inaccurate-">Why is zooming or clicking in the map off/inaccurate?</h2>
<p>OpenLayers does not update the map when the container element is resized. This can be caused by progressive updates
to CSS styles or manually resizing the map. When that happens, any interaction will become inaccurate: the map would zoom in and out, and end up not being centered on the pointer. This makes it hard to do certain interactions, e.g. selecting the desired feature.</p>
<p>There is currently no built-in way to react to element&#39;s size changes, as <a href="https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver">Resize Observer API</a> is only implemented in Chrome.</p>
<p>There is however an easy to use <a href="https://github.com/que-etc/resize-observer-polyfill">polyfill</a>:</p>
<pre><code class="language-javascript">import Map from <span class="string">'ol/Map'</span>;
import ResizeObserver from <span class="string">'resize-observer-polyfill'</span>;
<span class="keyword">const</span> mapElement = document.querySelector(<span class="string">'#map'</span>)
<span class="keyword">const</span> map = <span class="keyword">new</span> Map({
target: mapElement
})
<span class="keyword">const</span> sizeObserver = <span class="keyword">new</span> ResizeObserver(() =&gt; {
map.updateSize()
})
sizeObserver.observe(mapElement)
<span class="comment">// called when the map is destroyed</span>
<span class="comment">// sizeObserver.disconnect()</span></code></pre>
</div>
<footer>
Code licensed under the <a href='http://www.tldrlegal.com/license/bsd-2-clause-license-(freebsd)'>2-Clause BSD</a>. All documentation <a href='http://creativecommons.org/licenses/by/3.0/'>CC BY 3.0</a>. Thanks to our <a href='/sponsors.html'>sponsors</a>.
<br>
<a href="https://www.netlify.com">
This site is powered by Netlify.
</a>
</footer>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min.js"></script>
</body>
</html>