{"id":1151,"date":"2020-09-15T14:19:21","date_gmt":"2020-09-15T12:19:21","guid":{"rendered":"https:\/\/davikingcode.com\/blog\/?p=1151"},"modified":"2020-09-15T14:20:47","modified_gmt":"2020-09-15T12:20:47","slug":"vectorized-extruded-and-spatialized-map-data","status":"publish","type":"post","link":"https:\/\/davikingcode.com\/blog\/vectorized-extruded-and-spatialized-map-data\/","title":{"rendered":"Vectorized, extruded, and spatialized map data."},"content":{"rendered":"\n<p><em>Or the GeoJSON, D3.js to Three.js pipeline.<\/em><\/p>\n\n\n\n<p>It has been a while since our latest <a href=\"https:\/\/threejs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">Three.js<\/a> project! We&#8217;re mostly playing with <a href=\"https:\/\/unity.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Unity<\/a>, but it&#8217;s always a nice feeling to get back to the web directly.<\/p>\n\n\n\n<p>For this project, we&#8217;d to display a 3D map of the France with some sites informations points. The first thought concerned displaying a 3D map, we known we will need some svg at some point but then how to display it using Three.js? Also how could we put the sites\/cities on the map correctly? Let see that!<\/p>\n\n\n\n<iframe loading=\"lazy\" id=\"inlineFrameExample\" src=\"https:\/\/davikingcode.com\/dl-works\/archives\/2020\/formapi-map\/\" width=\"800\" height=\"600\">\n<\/iframe>\n\n\n\n<!--more-->\n\n\n\n<p>We could easily find a svg for the France&#8217;s map, but it must be well optimized and we need to have the regions (so we can colorize them). Also having real data, than just some &#8220;paths&#8221;, with latitude, longitude was really important. So we started with the <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/GeoJSON\" target=\"_blank\">GeoJSON<\/a> file format, and <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/gregoiredavid\/france-geojson\/blob\/master\/regions-version-simplifiee.geojson\" target=\"_blank\">grabbed this version<\/a>. Great, we have the data but now we need to visualize our map!<\/p>\n\n\n\n<p>Obviously we started to have a look on Three.js plugin for displaying a GeoJSON map, but didn&#8217;t find one working correctly with a <a rel=\"noreferrer noopener\" href=\"https:\/\/en.wikipedia.org\/wiki\/Mercator_projection\" target=\"_blank\">Mercator Projection<\/a>. So we get back to our first intuition concerning svg. We know that Three.js has a <a rel=\"noreferrer noopener\" href=\"https:\/\/threejs.org\/docs\/#examples\/en\/loaders\/SVGLoader\" target=\"_blank\">SVGLoader<\/a>, and then we can <a rel=\"noreferrer noopener\" href=\"https:\/\/threejs.org\/docs\/#api\/en\/geometries\/ExtrudeGeometry\" target=\"_blank\">Extrude<\/a> its geometry for having a 3D model, but how can we convert our GeoJSON file to a SVG map?<\/p>\n\n\n\n<p><a rel=\"noreferrer noopener\" href=\"https:\/\/d3js.org\/\" target=\"_blank\">D3.js<\/a> to the rescue! This is a powerful library for manipulating data, it includes SVG support and many map utilities! So from our GeoJSON file, we turn it at runtime into a SVG, that way we can tweak settings quickly!<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; gutter: false; title: ; notranslate\" title=\"\">\nconst geojson = JSON.parse(geojsonText);\n\n\/\/ we translate the projection at 0, 0, so the map is centered on our screen\nconst projection = geoMercator().scale(700).center(geoCentroid(geojson)).translate(&#91;0, 0]);\nconst pathGenerator = geoPath().projection(projection);\n\nlet mySvg = &#039;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;&lt;svg&gt;&lt;g&gt;&#039;;\nfor (let i = 0; i &lt; geojson.features.length; i++)\n\tmySvg += &quot;&lt;path id=\\&quot;&quot; + (i + 1) + &quot;\\&quot; name=\\&quot;&quot; + geojson.features&#91;i].properties.nom + &quot;\\&quot; d=\\&quot;&quot; + pathGenerator(geojson.features&#91;i]) + &quot;\\&quot;\\\/&gt;&quot;;\nmySvg += &quot;&lt;\/g&gt;&lt;\/svg&gt;&quot;;\n\n\/\/ now load the SVG and display the map\nconst paths = new SVGLoader(manager).parse(mySvg).paths;\n\nconst group = new Group();\ngroup.scale.y *= - 1;\n\nlet regionGeometry = null;\nlet regionMesh = null;\nconst regionTextMaterial = new MeshBasicMaterial({ color: new Color(0x666666) });\n\nconst shapeMaterial = new MeshBasicMaterial({ vertexColors: true, depthWrite: true });\n\nfor (let i = 0; i &lt; paths.length; ++i)\n{\n    const path = paths&#91;i];\n\n    const region = data.regions.find(x =&gt; x.id == path.userData.node.id);\n    const color = new Color(region.color); \/\/ we grab some data from a static file\n\n    \/\/ draw region\n    const shapes = path.toShapes(true);\n\n    for (let j = 0; j &lt; shapes.length; ++j)\n    {\n        const geometry = new ExtrudeGeometry(shapes&#91;j], { depth: 0.001, bevelSize: 0 });\n\n        for (let k = 0; k &lt; geometry.faces.length; ++k)\n            geometry.faces&#91;k].vertexColors = &#91;color, color, color];\n\n        if (regionGeometry == null)\n        {\n            regionGeometry = geometry\n            regionMesh = new Mesh(regionGeometry, shapeMaterial);\n            regionMesh.name = &quot;regions&quot;\n        }\n        else\n            regionMesh.geometry.merge(geometry);\n    }\n}\n\ngroup.add(regionMesh);\nscene.add(group);\n<\/pre><\/div>\n\n\n<p>Et voil\u00e0! Did you notice the <em>merge geometry<\/em> function? Even if we&#8217;re are drawing several regions, they are considered as one mesh. This is an important optimization tips, you can read more about it on this <a rel=\"noreferrer noopener\" href=\"https:\/\/threejsfundamentals.org\/threejs\/lessons\/threejs-optimize-lots-of-objects.html\" target=\"_blank\">awesome website<\/a>.<\/p>\n\n\n\n<h2>Displaying sites and informations<\/h2>\n\n\n\n<p>We&#8217;d an Excel file from the client with all the sites and theirs informations (type, adress, phone, contact, logo etc.). Since we&#8217;ve a real map, and many sites, the quickest way to display to them wouldn&#8217;t be to position them via their GPS coordinate?<\/p>\n\n\n\n<p>The french governement provides an <a rel=\"noreferrer noopener\" href=\"https:\/\/geo.api.gouv.fr\/adresse\" target=\"_blank\">API<\/a> for doing some geocoding. We made a quick script in PHP for grabbing all addresses. We used the <a rel=\"noreferrer noopener\" href=\"http:\/\/docs.guzzlephp.org\/en\/stable\/\" target=\"_blank\">Guzzle<\/a> library making HTTP request very easily!<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: php; gutter: false; title: ; notranslate\" title=\"\">\nrequire_once(&#039;guzzle\/autoloader.php&#039;);\n$client = new GuzzleHttp\\Client(&#91;&#039;verify&#039; =&gt; false]);\n\n$request = &#039;https:\/\/api-adresse.data.gouv.fr\/search\/?q=&#039;.$datas&#91;&quot;sites&quot;]&#91;$i]&#91;&quot;adress&quot;];\n$response = $client-&gt;request(&#039;GET&#039;, $request);\n$data = json_decode($response-&gt;getBody());\n\nif (count($data-&gt;features) &gt; 0)\n{\n    $datas&#91;&quot;sites&quot;]&#91;$i]&#91;&quot;lat&quot;] = $data-&gt;features&#91;0]-&gt;geometry-&gt;coordinates&#91;1];\n    $datas&#91;&quot;sites&quot;]&#91;$i]&#91;&quot;long&quot;] = $data-&gt;features&#91;0]-&gt;geometry-&gt;coordinates&#91;0];\n}\n\n\/\/ https:\/\/stackoverflow.com\/questions\/6054033\/pretty-printing-json-with-php\nif (version_compare(phpversion(), &#039;7.1&#039;, &#039;&gt;=&#039;))\n    ini_set( &#039;serialize_precision&#039;, -1 );\n\nfile_put_contents(&#039;..\/src\/models\/data.json&#039;, json_encode($datas, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));\n<\/pre><\/div>","protected":false},"excerpt":{"rendered":"<p>Or the GeoJSON, D3.js to Three.js pipeline. It has been a while since our latest Three.js project! We&#8217;re mostly playing with Unity, but it&#8217;s always a nice feeling to get back to the web directly. For this project, we&#8217;d to display a 3D map of the France with some sites informations points. The first thought &hellip; <a href=\"https:\/\/davikingcode.com\/blog\/vectorized-extruded-and-spatialized-map-data\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Vectorized, extruded, and spatialized map data.<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_discordance_state":"","_discordance_checked":true},"categories":[15],"tags":[58,29],"_links":{"self":[{"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/posts\/1151"}],"collection":[{"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/comments?post=1151"}],"version-history":[{"count":17,"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/posts\/1151\/revisions"}],"predecessor-version":[{"id":1170,"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/posts\/1151\/revisions\/1170"}],"wp:attachment":[{"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/media?parent=1151"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/categories?post=1151"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/davikingcode.com\/blog\/wp-json\/wp\/v2\/tags?post=1151"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}