{"id":321740,"date":"2021-04-20T09:00:18","date_gmt":"2021-04-20T09:00:18","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=321740"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=321740","title":{"rendered":"Deck.gl, Mapbox \u0438 React: \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0442\u043e\u0447\u0435\u043a, \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432 \u0438 \u043a\u043b\u0430\u0441\u0442\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0430 \u043a\u0430\u0440\u0442\u0435"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\"><b>Mapbox<\/b> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u043c\u0435\u0440\u0438\u043a\u0430\u043d\u0441\u043a\u0438\u043c \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u043e\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u043e\u043d\u043b\u0430\u0439\u043d-\u043a\u0430\u0440\u0442 \u0434\u043b\u044f \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u043e\u0432 \u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u0421 2010 \u0433\u043e\u0434\u0430 \u043e\u043d \u0431\u044b\u0441\u0442\u0440\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u043b \u043d\u0438\u0448\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u043a\u0430\u0440\u0442, \u0432 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440, \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430\u043c\u0438 \u043a\u0430\u0440\u0442, \u0442\u0430\u043a\u0438\u043c\u0438 \u043a\u0430\u043a Google Maps. \u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0434\u043e\u0441\u0442\u043e\u0439\u043d\u044b\u043c \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u043c \u043d\u0430 \u0444\u043e\u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043e\u0432.<\/p>\n<ul>\n<li><a href=\"https:\/\/www.mapbox.com\/\" rel=\"nofollow noopener noreferrer\">\u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0430\u0439\u0442<\/a><\/li>\n<\/ul>\n<p>  <b>Deck.gl<\/b> \u044d\u0442\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u043d\u0430 \u0431\u0430\u0437\u0435 WEB API WebGL \u0434\u043b\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445). \u0421\u043e\u0437\u0434\u0430\u043d\u0430 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f Uber. \u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Mapbox. \u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f Google Maps, \u043d\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0431\u0443\u0434\u0443\u0442 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u044b.<\/p>\n<ul>\n<li><a href=\"https:\/\/deck.gl\/\" rel=\"nofollow noopener noreferrer\">\u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0430\u0439\u0442<\/a><\/li>\n<\/ul>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u043a \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043c \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0443\u043d\u043a\u0442\u043e\u0432. \u041d\u0430\u043f\u043e\u043c\u043d\u044e, \u0447\u0442\u043e \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c React.<\/p>\n<p>  1. \u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u044b<\/p>\n<p>  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 Mapbox \u0438 \u0432 \u043b\u0438\u0447\u043d\u043e\u043c \u043a\u0430\u0431\u0438\u043d\u0435\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d. \u041e\u043d \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u0430\u0440\u0442\u0430\u043c\u0438 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435. <br \/>  <a name=\"habracut\"><\/a><br \/>  \u041f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u043c \u043a \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439: <code>yarn add mapbox-gl @urbica\/react-map-gl<\/code><\/p>\n<p>  \u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u043a\u043e\u0434 \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u043a\u0430\u0440\u0442\u044b \u043d\u0430 \u0432\u0435\u0441\u044c \u044d\u043a\u0440\u0430\u043d \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">import * as React from &quot;react&quot;; import MapGL from &quot;@urbica\/react-map-gl&quot;;  import &quot;mapbox-gl\/dist\/mapbox-gl.css&quot;;  const App = () =&gt; {  const viewport = {    latitude: 0,    longitude: 0,    zoom: 1,  };   return (    &lt;MapGL      style={{ width: &quot;100vw&quot;, height: &quot;100vh&quot; }}      accessToken={TOKEN}      {...viewport}    \/&gt;  ); };<\/code><\/pre>\n<p>  2. \u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0442\u043e\u0447\u0435\u043a<\/p>\n<p>  \u0411\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Deck.gl \u0441\u043b\u043e\u0439 IconLayer.<\/p>\n<p>  \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e \u043d\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/deck.gl\/docs\/api-reference\/layers\/icon-layer\" rel=\"nofollow noopener noreferrer\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>  \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438: <code>yarn add @deck.gl\/mapbox @deck.gl\/layers<\/code><br \/>  \u0410\u0442\u043b\u0430\u0441 \u0441 \u0438\u043a\u043e\u043d\u043a\u0430\u043c\u0438 \u0432\u043e\u0437\u044c\u043c\u0435\u043c \u043f\u043e <a href=\"https:\/\/raw.githubusercontent.com\/visgl\/deck.gl-data\/master\/website\/icon-atlas.png\" rel=\"nofollow noopener noreferrer\">\u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<p>  \u0412 \u043e\u0431\u044a\u0435\u043a\u0442\u0435 ICON_MAPPING \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0432\u0441\u0435 \u0432\u0438\u0434\u044b \u0438\u043a\u043e\u043d\u043e\u043a \u0438 \u0438\u0445 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0432 \u0430\u0442\u043b\u0430\u0441\u0435 \u0432\u044b\u0448\u0435.<\/p>\n<p>  \u041c\u0430\u0441\u0441\u0438\u0432 \u0434\u043b\u044f \u0434\u0432\u0443\u0445 \u0442\u043e\u0447\u0435\u043a \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c. \u041e\u0431\u0440\u0430\u0449\u0430\u044e \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0432 \u043a\u043b\u044e\u0447\u0435 icon \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0432\u0438\u0434 \u0438\u043a\u043e\u043d\u043a\u0438 \u0438\u0437 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 ICON_MAPPING.<\/p>\n<pre><code class=\"javascript\">const iconsData = [  {    id: 1,    name: &quot;First Point&quot;,    size: 5,    icon: &quot;marker&quot;,    coordinates: [-100.12097640000002, 35.449965],    color: [0, 0, 128],  },  {    id: 2,    name: &quot;Second Point&quot;,    size: 5,    icon: &quot;marker&quot;,    coordinates: [-100.0893059, 40.39611790000001],    color: [255, 0, 0],  }, ];<\/code><\/pre>\n<p>  <b>\u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u043a\u043e\u0434:<\/b><\/p>\n<pre><code class=\"javascript\">import * as React from &quot;react&quot;; import MapGL, { CustomLayer } from &quot;@urbica\/react-map-gl&quot;; import { MapboxLayer } from &quot;@deck.gl\/mapbox&quot;; import { IconLayer } from &quot;@deck.gl\/layers&quot;;  import &quot;mapbox-gl\/dist\/mapbox-gl.css&quot;;  import Atlas from &quot;..\/src\/img\/icon-atlas.png&quot;; import iconsData from &quot;.\/layersData\/iconsData&quot;;  const ICON_MAPPING = {  marker: { x: 0, y: 0, width: 140, height: 148, mask: true },  circle: { x: 0, y: 130, width: 120, height: 120, mask: true }, };  const App = () =&gt; {  const [viewport, setViewport] = React.useState({    latitude: 0,    longitude: 0,    zoom: 1,  });   const iconsLayer = new MapboxLayer({    id: &quot;icon-layer&quot;,    type: IconLayer,    data: iconsData,    iconAtlas: Atlas,    sizeScale: 10,    iconMapping: ICON_MAPPING,    getIcon: (d) =&gt; d.icon,    getPosition: (d) =&gt; d.coordinates,    getSize: (d) =&gt; d.size,    getColor: (d) =&gt; d.color,  });   return (    &lt;MapGL      style={{ width: &quot;100vw&quot;, height: &quot;100vh&quot; }}      accessToken={TOKEN}      onViewportChange={setViewport}      {...viewport}    &gt;      &lt;CustomLayer layer={iconsLayer} \/&gt;    &lt;\/MapGL&gt;  ); };<\/code><\/pre>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/su\/wk\/dt\/suwkdtup6kmk1a_oirwnsqo4m64.png\"><\/p>\n<p>  3. \u041f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u043e\u0432<\/p>\n<p>  \u0414\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c TripsLayer <a href=\"https:\/\/deck.gl\/docs\/api-reference\/geo-layers\/trips-layer\" rel=\"nofollow noopener noreferrer\">\u0441\u043b\u043e\u0439<\/a>.<\/p>\n<p>  \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u044f \u0441\u043e\u0437\u0434\u0430\u044e \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u043c\u0438 \u043c\u0430\u0441\u0441\u0438\u0432\u0430\u043c\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u043d\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 map \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0441\u043b\u043e\u044f \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u0441 \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u043a\u0430\u043a \u0432 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435. \u0425\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u043e\u0439 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0435 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0441\u0432\u043e\u0435 \u0432\u0440\u0435\u043c\u044f (unix timestamp).<\/p>\n<pre><code class=\"javascript\">const tripsData = [  {    coordinates: [      [-100.149639, 35.440481],      [-100.151832, 35.439649],      [-100.152752, 35.439323],      [-100.154222, 35.439699],      [-100.154293, 35.439301],      [-100.15539, 35.438506],      [-100.155745, 35.43833],      [-100.156138, 35.4382],      [-100.157342, 35.43783],      [-100.157729, 35.437787],      [-100.15931, 35.438335],      [-100.159402, 35.438002],      [-100.159333, 35.437877],      [-100.159702, 35.437776],      [-100.160215, 35.437766],      [-100.160195, 35.43783],      [-100.160439, 35.43806],      [-100.160269, 35.437808],      [-100.160124, 35.438099],      [-100.143509, 35.441997],      [-100.142375, 35.442401],      [-100.141645, 35.442632],      [-100.141291, 35.442684],      [-100.141841, 35.442592],      [-100.139913, 35.443157],      [-100.139481, 35.443198],      [-100.138995, 35.443288],      [-100.138686, 35.443415],    ],    timestamps: [      1556009520,      1556009520,      1556009520,      1556009520,      1556009520,      1556009520,      1556009520,      1556009520,      1556009520,      1556009520,      1556009880,      1556009880,      1556010060,      1556011440,      1556011440,      1556011500,      1556011500,      1556011500,      1556011680,      1556012100,      1556012160,      1556012160,      1556012160,      1556012160,      1556012160,      1556012160,      1556012160,      1556012160,    ],    color: [18, 83, 2],  }, ];<\/code><\/pre>\n<p>  \u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u043a\u043e\u0434. \u041e\u0431\u0440\u0430\u0449\u0430\u044e \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0432 \u043a\u043b\u044e\u0447\u0435 <i>currentTime<\/i> \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u044f \u044f \u0443\u043a\u0430\u0437\u0430\u043b \u0434\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0432\u0440\u0435\u043c\u044f unix timestamp \u0438\u0437 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0432\u044b\u0448\u0435.<\/p>\n<pre><code class=\"javascript\">import * as React from &quot;react&quot;; import MapGL, { CustomLayer } from &quot;@urbica\/react-map-gl&quot;; import { MapboxLayer } from &quot;@deck.gl\/mapbox&quot;; import { TripsLayer } from &quot;@deck.gl\/geo-layers&quot;;  import &quot;mapbox-gl\/dist\/mapbox-gl.css&quot;;  import tripsData from &quot;.\/layersData\/tripsData&quot;;  const App = () =&gt; {  const [viewport, setViewport] = React.useState({    latitude: 0,    longitude: 0,    zoom: 1,  });   const tripsLayer = new MapboxLayer({    id: &quot;trips-layer&quot;,    type: TripsLayer,    data: tripsData,    widthMinPixels: 3,    rounded: true,    trailLength: 200,    currentTime: 1556012340,    getPath: (d) =&gt; d.coordinates,    getTimestamps: (d) =&gt; d.timestamps,    getColor: (d) =&gt; d.color,  });   return (    &lt;MapGL      style={{ width: &quot;100vw&quot;, height: &quot;100vh&quot; }}      accessToken={TOKEN}      onViewportChange={setViewport}      {...viewport}    &gt;      &lt;CustomLayer layer={tripsLayer} \/&gt;    &lt;\/MapGL&gt;  ); };<\/code><\/pre>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/b2\/qr\/zm\/b2qrzmj4ikkorwdqrvykxrrakiw.png\"><\/p>\n<p>  4. \u041a\u043b\u0430\u0441\u0442\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u044f<\/p>\n<p>  \u0414\u043b\u044f \u0435\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438: <code>yarn add supercluster @urbica\/react-map-gl-cluster<\/code><\/p>\n<p>  \u041f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/github.com\/urbica\/react-map-gl-cluster\" rel=\"nofollow noopener noreferrer\">\u0437\u0434\u0435\u0441\u044c<\/a>.<\/p>\n<p>  \u0414\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0442\u043e\u0447\u0435\u043a \u0437\u0430\u043f\u0438\u0448\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"javascript\">const clusterData = [  {    id: 1,    name: &quot;First Point&quot;,    coordinates: [-110.12097640000002, 35.449965],  },  {    id: 2,    name: &quot;Second Point&quot;,    coordinates: [-112.0893059, 35.39611790000001],  }, ]; <\/code><\/pre>\n<p>  \u041d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0442\u043e\u0447\u043a\u0443, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0443\u044e \u0432 \u0441\u0435\u0431\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u0440\u0443\u0433\u0438\u0445. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u043d\u0430 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0435 \u0442\u043e\u0447\u043a\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0432 \u0441\u0435\u0431\u0435 \u0434\u0432\u0435 \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u0435\u0434\u0438\u043d\u0438\u0447\u043d\u044b\u0435 \u0438 \u043f\u0440\u0438 \u043f\u0440\u0438\u0431\u043b\u0438\u0436\u0435\u043d\u0438\u0438 \u0440\u0430\u0441\u043f\u0430\u0434\u0430\u0435\u0442\u0441\u044f.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/he\/f4\/b9\/hef4b9oub_2gkyljcwlxvg8ggq8.png\"><\/p>\n<pre><code class=\"javascript\">import React from &quot;react&quot;; import { Marker } from &quot;@urbica\/react-map-gl&quot;;  import { pointStyle } from &quot;.\/App&quot;;  const ClusterMarker = (props) =&gt; (  &lt;Marker longitude={props.longitude} latitude={props.latitude}&gt;    &lt;div style={pointStyle}&gt;{props.pointCount}&lt;\/div&gt;  &lt;\/Marker&gt; );<\/code><\/pre>\n<p>  \u0413\u043b\u0430\u0432\u043d\u0430\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a. \u0414\u043b\u044f \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0441\u0442\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442:<\/p>\n<pre><code class=\"javascript\">import * as React from &quot;react&quot;; import MapGL, { Marker } from &quot;@urbica\/react-map-gl&quot;; import Cluster from &quot;@urbica\/react-map-gl-cluster&quot;;  import &quot;mapbox-gl\/dist\/mapbox-gl.css&quot;;  import ClusterMarker from &quot;.\/ClusterMarker&quot;; import clusterData from &quot;.\/layersData\/clusterData&quot;;  export const pointStyle = {  display: &quot;flex&quot;,  justifyContent: &quot;center&quot;,  alignItems: &quot;center&quot;,  width: &quot;32px&quot;,  height: &quot;32px&quot;,  borderRadius: &quot;50%&quot;,  border: &quot;1px solid black&quot;,  backgroundColor: &quot;white&quot;, };  const App = () =&gt; {  const [viewport, setViewport] = React.useState({    latitude: 0,    longitude: 0,    zoom: 1,  });   const clusterLayerData = React.useMemo(    () =&gt;      clusterData.map((point) =&gt; {        const [fCoordinate, sCoordinate] = point.coordinates;        const lng =          sCoordinate &gt; -90 &amp;&amp; sCoordinate &lt; 90 ? fCoordinate : sCoordinate;        const lat = lng === fCoordinate ? sCoordinate : fCoordinate;         return (          &lt;Marker key={point.id} longitude={lng} latitude={lat}&gt;            &lt;div style={pointStyle}&gt;1&lt;\/div&gt;          &lt;\/Marker&gt;        );      }),    []  );   return (    &lt;MapGL      style={{ width: &quot;100vw&quot;, height: &quot;100vh&quot; }}      accessToken={TOKEN}      onViewportChange={setViewport}      {...viewport}    &gt;      &lt;Cluster        radius={40}        extent={512}        nodeSize={64}        component={ClusterMarker}        children={clusterLayerData}      \/&gt;    &lt;\/MapGL&gt;  ); };<\/code><\/pre>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/bt\/yd\/9r\/btyd9rrjbotow-fibjlrgnedmxk.png\"><\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/gb\/em\/ly\/gbemlycjvdg_lal1drj7orakbb4.png\"><\/p>\n<p>  <b>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<br \/>  <\/b><br \/>  \u041d\u0430 \u0441\u0430\u0439\u0442\u0435 Deck.gl \u0435\u0449\u0435 \u0441\u043e\u0431\u0440\u0430\u043d\u043e \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437\u043d\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u044b\u0445 \u0441\u043b\u043e\u0435\u0432. \u041d\u043e \u043a\u0430\u043a \u0432\u044b \u043c\u043e\u0433\u043b\u0438 \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Deck.gl \u0438 Mapbox \u043c\u043e\u0436\u043d\u043e \u0441 \u043b\u0435\u0433\u043a\u043e\u0441\u0442\u044c\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u043d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b. \u0423\u0441\u043f\u0435\u0445\u043e\u0432!<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/553126\/\"> https:\/\/habr.com\/ru\/post\/553126\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\"><b>Mapbox<\/b> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u043c\u0435\u0440\u0438\u043a\u0430\u043d\u0441\u043a\u0438\u043c \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u043e\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u043e\u043d\u043b\u0430\u0439\u043d-\u043a\u0430\u0440\u0442 \u0434\u043b\u044f \u0432\u0435\u0431-\u0441\u0430\u0439\u0442\u043e\u0432 \u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u0421 2010 \u0433\u043e\u0434\u0430 \u043e\u043d \u0431\u044b\u0441\u0442\u0440\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u043b \u043d\u0438\u0448\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u043a\u0430\u0440\u0442, \u0432 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440, \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430\u043c\u0438 \u043a\u0430\u0440\u0442, \u0442\u0430\u043a\u0438\u043c\u0438 \u043a\u0430\u043a Google Maps. \u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0434\u043e\u0441\u0442\u043e\u0439\u043d\u044b\u043c \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u043e\u043c \u043d\u0430 \u0444\u043e\u043d\u0435 \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0430 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043e\u0432.<\/p>\n<ul>\n<li><a href=\"https:\/\/www.mapbox.com\/\" rel=\"nofollow noopener noreferrer\">\u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0430\u0439\u0442<\/a><\/li>\n<\/ul>\n<p>  <b>Deck.gl<\/b> \u044d\u0442\u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u043d\u0430 \u0431\u0430\u0437\u0435 WEB API WebGL \u0434\u043b\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0430\u043d\u0430\u043b\u0438\u0437\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043d\u0430\u0431\u043e\u0440\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445 (\u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0433\u0435\u043e\u0433\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445). \u0421\u043e\u0437\u0434\u0430\u043d\u0430 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044f Uber. \u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u0430 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c Mapbox. \u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0442\u0441\u044f Google Maps, \u043d\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0431\u0443\u0434\u0443\u0442 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u044b.<\/p>\n<ul>\n<li><a href=\"https:\/\/deck.gl\/\" rel=\"nofollow noopener noreferrer\">\u041e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0430\u0439\u0442<\/a><\/li>\n<\/ul>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u043a \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043c \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0443\u043d\u043a\u0442\u043e\u0432. \u041d\u0430\u043f\u043e\u043c\u043d\u044e, \u0447\u0442\u043e \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c React.<\/p>\n<p>  1. \u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u044b<\/p>\n<p>  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u0430\u0439\u0442\u0435 Mapbox \u0438 \u0432 \u043b\u0438\u0447\u043d\u043e\u043c \u043a\u0430\u0431\u0438\u043d\u0435\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d. \u041e\u043d \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u0430\u0440\u0442\u0430\u043c\u0438 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435.   <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-321740","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/321740","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=321740"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/321740\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=321740"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=321740"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=321740"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}