{"id":154,"date":"2024-08-14T06:46:50","date_gmt":"2024-08-14T06:46:50","guid":{"rendered":"https:\/\/research.binus.ac.id\/geoecoai\/?p=154"},"modified":"2024-08-14T07:52:12","modified_gmt":"2024-08-14T07:52:12","slug":"7-create-search-bar-feature","status":"publish","type":"post","link":"https:\/\/research.binus.ac.id\/geoecoai\/2024\/08\/14\/7-create-search-bar-feature\/","title":{"rendered":"7. Create Search Bar and Print Feature"},"content":{"rendered":"<h3>Tutorial: Implementing a Search Feature with React and Leaflet<\/h3>\n<p>In this tutorial, we will build a <strong>Search<\/strong> component that allows users to search for locations and display the results on a map with a popup containing location information. We will use React, React-Leaflet, and leaflet-geosearch for this component.<\/p>\n<h4>1. Import Required Libraries<\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-title class_\">React<\/span>, { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> { useMap } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-leaflet\"<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> { <span class=\"hljs-title class_\">OpenStreetMapProvider<\/span> } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"leaflet-geosearch\"<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> L <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"leaflet\"<\/span>;<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li><code>useState<\/code>: React hook for managing local state.<\/li>\n<li><code>useMap<\/code>: Hook from React-Leaflet to get the map instance.<\/li>\n<li><code>OpenStreetMapProvider<\/code>: Provider from leaflet-geosearch for geocoding locations based on text.<\/li>\n<li><code>L<\/code>: Leaflet shorthand for accessing APIs like popup and view settings.<\/li>\n<\/ul>\n<h4>2. Initialize the Search Component<\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-title function_\">Search<\/span> = () =&gt; {<br \/>\n<span class=\"hljs-keyword\">const<\/span> map = <span class=\"hljs-title function_\">useMap<\/span>();<br \/>\n<span class=\"hljs-keyword\">const<\/span> [searchText, setSearchText] = <span class=\"hljs-title function_\">useState<\/span>(<span class=\"hljs-string\">\"\"<\/span>);<br \/>\n<span class=\"hljs-keyword\">const<\/span> [popup, setPopup] = <span class=\"hljs-title function_\">useState<\/span>(<span class=\"hljs-literal\">null<\/span>);<br \/>\n<span class=\"hljs-keyword\">const<\/span> [suggestions, setSuggestions] = <span class=\"hljs-title function_\">useState<\/span>([]);<br \/>\n<span class=\"hljs-keyword\">const<\/span> [isExpanded, setIsExpanded] = <span class=\"hljs-title function_\">useState<\/span>(<span class=\"hljs-literal\">false<\/span>);<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li><code>map<\/code>: Gets the map instance from <code>useMap<\/code>.<\/li>\n<li><code>searchText<\/code>: State to store the user&#8217;s search input.<\/li>\n<li><code>popup<\/code>: State to store the active popup reference on the map.<\/li>\n<li><code>suggestions<\/code>: State to store location suggestions based on the search text.<\/li>\n<li><code>isExpanded<\/code>: State to manage the display of the search input field.<\/li>\n<\/ul>\n<h4>3. Handle Search Input Changes<\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-title function_\">handleSearchInputChange<\/span> = (<span class=\"hljs-params\">e<\/span>) =&gt; {<br \/>\n<span class=\"hljs-keyword\">const<\/span> userInput = e.<span class=\"hljs-property\">target<\/span>.<span class=\"hljs-property\">value<\/span>;<br \/>\n<span class=\"hljs-title function_\">setSearchText<\/span>(userInput);<br \/>\n<span class=\"hljs-keyword\">const<\/span> provider = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title class_\">OpenStreetMapProvider<\/span>();<br \/>\nprovider<br \/>\n.<span class=\"hljs-title function_\">search<\/span>({ <span class=\"hljs-attr\">query<\/span>: userInput })<br \/>\n.<span class=\"hljs-title function_\">then<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">results<\/span>) =&gt;<\/span> {<br \/>\n<span class=\"hljs-keyword\">const<\/span> suggestions = results.<span class=\"hljs-title function_\">map<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">result<\/span>) =&gt;<\/span> result.<span class=\"hljs-property\">label<\/span>);<br \/>\n<span class=\"hljs-title function_\">setSuggestions<\/span>(suggestions);<br \/>\n})<br \/>\n.<span class=\"hljs-title function_\">catch<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">error<\/span>) =&gt;<\/span> {<br \/>\n<span class=\"hljs-variable language_\">console<\/span>.<span class=\"hljs-title function_\">error<\/span>(<span class=\"hljs-string\">\"Error fetching search suggestions:\"<\/span>, error);<br \/>\n<span class=\"hljs-title function_\">setSuggestions<\/span>([]);<br \/>\n});<br \/>\n};<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li>Updates <code>searchText<\/code> whenever the user types.<\/li>\n<li>Uses <code>OpenStreetMapProvider<\/code> to fetch location suggestions based on user input.<\/li>\n<li>Stores the suggestions in the <code>suggestions<\/code> state.<\/li>\n<\/ul>\n<h4>4. Handle Clicks on Location Suggestions<\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-title function_\">handleSuggestionClick<\/span> = (<span class=\"hljs-params\">suggestion<\/span>) =&gt; {<br \/>\n<span class=\"hljs-title function_\">setSearchText<\/span>(suggestion);<br \/>\n<span class=\"hljs-title function_\">setSuggestions<\/span>([]);<br \/>\n};<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li>Updates <code>searchText<\/code> when a user selects a suggestion and clears the suggestions list.<\/li>\n<\/ul>\n<h4>5. Handle Search Form Submission<\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-title function_\">handleSearch<\/span> = <span class=\"hljs-keyword\">async<\/span> (<span class=\"hljs-params\">e<\/span>) =&gt; {<br \/>\ne.<span class=\"hljs-title function_\">preventDefault<\/span>();<br \/>\n<span class=\"hljs-keyword\">if<\/span> (!searchText.<span class=\"hljs-title function_\">trim<\/span>()) <span class=\"hljs-keyword\">return<\/span>;<br \/>\n<span class=\"hljs-keyword\">const<\/span> provider = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-title class_\">OpenStreetMapProvider<\/span>();<br \/>\n<span class=\"hljs-keyword\">const<\/span> results = <span class=\"hljs-keyword\">await<\/span> provider.<span class=\"hljs-title function_\">search<\/span>({ <span class=\"hljs-attr\">query<\/span>: searchText });<br \/>\n<span class=\"hljs-keyword\">if<\/span> (results.<span class=\"hljs-property\">length<\/span> &gt; <span class=\"hljs-number\">0<\/span>) {<br \/>\n<span class=\"hljs-keyword\">const<\/span> { x, y, label } = results[<span class=\"hljs-number\">0<\/span>];<br \/>\n<span class=\"hljs-keyword\">const<\/span> latLng = L.<span class=\"hljs-title function_\">latLng<\/span>(y, x);<br \/>\nmap.<span class=\"hljs-title function_\">setView<\/span>(latLng, <span class=\"hljs-number\">10<\/span>);<br \/>\n<span class=\"hljs-keyword\">if<\/span> (popup) {<br \/>\npopup.<span class=\"hljs-title function_\">removeFrom<\/span>(map);<br \/>\n}<br \/>\n<span class=\"hljs-keyword\">const<\/span> newPopup = L.<span class=\"hljs-title function_\">popup<\/span>()<br \/>\n.<span class=\"hljs-title function_\">setLatLng<\/span>(latLng)<br \/>\n.<span class=\"hljs-title function_\">setContent<\/span>(<br \/>\n<span class=\"hljs-string\">`&lt;b&gt;<span class=\"hljs-subst\">${label}<\/span>&lt;\/b&gt;&lt;br&gt;Latitude: <span class=\"hljs-subst\">${y.toFixed(<span class=\"hljs-number\">6<\/span>)}<\/span>, Longitude: <span class=\"hljs-subst\">${x.toFixed(<span class=\"hljs-number\">6<\/span>)}<\/span>`<\/span><br \/>\n)<br \/>\n.<span class=\"hljs-title function_\">openOn<\/span>(map);<br \/>\n<span class=\"hljs-title function_\">setPopup<\/span>(newPopup);<br \/>\n}<br \/>\n};<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li>Searches for the location based on the input text when the form is submitted.<\/li>\n<li>If a location is found, updates the map view to the location and displays a popup with location information.<\/li>\n<\/ul>\n<h4>6. Clear Search Input<\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-title function_\">clearInput<\/span> = () =&gt; {<br \/>\n<span class=\"hljs-title function_\">setSearchText<\/span>(<span class=\"hljs-string\">''<\/span>);<br \/>\n<span class=\"hljs-title function_\">setIsExpanded<\/span>(<span class=\"hljs-literal\">false<\/span>);<br \/>\n};<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li>Clears the search input and resets the input field to its default state.<\/li>\n<\/ul>\n<h4>7. Render the Search Component<\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\">  <span class=\"hljs-keyword\">return<\/span> (<br \/>\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">onSubmit<\/span>=<span class=\"hljs-string\">{handleSearch}<\/span> <span class=\"hljs-attr\">onMouseEnter<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> setIsExpanded(true)}<br \/>\nonMouseLeave={() =&gt; setIsExpanded(searchText !== '')} style={{<br \/>\nposition: \"absolute\",<br \/>\ntop: \"1%\",<br \/>\nleft: \"94%\",<br \/>\ntransform: \"translate(-50%, 0)\",<br \/>\nzIndex: 1000,<br \/>\n}}&gt;<br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span><br \/>\n<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"srch\"<\/span><br \/>\n<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span><br \/>\n<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{searchText}<\/span><br \/>\n<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{handleSearchInputChange}<\/span><br \/>\n<span class=\"hljs-attr\">list<\/span>=<span class=\"hljs-string\">\"searchSuggestions\"<\/span><br \/>\n<span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Search...\"<\/span><br \/>\n<span class=\"hljs-attr\">required<\/span><br \/>\n\/&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">datalist<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"searchSuggestions\"<\/span>&gt;<\/span><br \/>\n{suggestions.map((suggestion, index) =&gt; (<br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{index}<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{suggestion}<\/span> \/&gt;<\/span><br \/>\n))}<br \/>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">datalist<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"fa fa-search\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span><br \/>\n<span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span><br \/>\n<span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{clearInput}<\/span><br \/>\n<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{isExpanded<\/span> ? \"<span class=\"hljs-attr\">clear-link<\/span> <span class=\"hljs-attr\">visible<\/span>\" <span class=\"hljs-attr\">:<\/span> \"<span class=\"hljs-attr\">clear-link<\/span>\"}<br \/>\n&gt;<\/span><br \/>\n<span class=\"hljs-symbol\">\u2715<\/span><br \/>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span><\/span><br \/>\n);<br \/>\n};<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li>Creates a search form with a text input, location suggestions, a submit button, and a clear button.<\/li>\n<li>Uses inline CSS to position the form over the map.<\/li>\n<\/ul>\n<h4>8. Export the Component<\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-title class_\">Search<\/span>;<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li>Exports the <code>Search<\/code> component for use in other parts of the application.<\/li>\n<\/ul>\n<p><span style=\"font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;font-style: normal;font-weight: 400\">This tutorial outlines how to build a location search feature using React and Leaflet, including fetching location data, showing suggestions, and updating the map with search results.<\/span><\/p>\n<p><!--more--><\/p>\n<article class=\"w-full text-token-text-primary focus-visible:outline-2 focus-visible:outline-offset-[-4px]\" dir=\"auto\" data-testid=\"conversation-turn-13\" data-scroll-anchor=\"false\">\n<div class=\"text-base py-[18px] px-3 md:px-4 m-auto md:px-5 lg:px-1 xl:px-5\">\n<div class=\"mx-auto flex flex-1 gap-4 text-base md:gap-5 lg:gap-6 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem]\">\n<div class=\"group\/conversation-turn relative flex w-full min-w-0 flex-col agent-turn\">\n<div class=\"flex-col gap-1 md:gap-3\">\n<div class=\"flex max-w-full flex-col flex-grow\">\n<div class=\"min-h-[20px] text-message flex w-full flex-col items-end gap-2 whitespace-pre-wrap break-words [.text-message+&amp;]:mt-5 overflow-x-auto\" dir=\"auto\" data-message-author-role=\"assistant\" data-message-id=\"334c5664-0dc9-4c08-bb70-cfe6f4e98367\">\n<div class=\"flex w-full flex-col gap-1 empty:hidden first:pt-[3px]\">\n<div class=\"markdown prose w-full break-words dark:prose-invert dark\">\n<h3>Tutorial: Capturing and Printing Map Images with React<\/h3>\n<p>In this tutorial, we&#8217;ll create a <strong>PrintLayer<\/strong> component that allows users to capture an image of the map and save it as a PNG file. This component uses <strong>html-to-image<\/strong> to convert a portion of the application into an image.<\/p>\n<h4>1. <strong>Import Required Libraries and Components<\/strong><\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> logo <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/Logo\/Printer.png\"<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> { <span class=\"hljs-title class_\">Map<\/span> } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/Map\/Map\"<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-title class_\">React<\/span>, { useState, useRef, useCallback } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> { toPng } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"html-to-image\"<\/span>;<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li><code>logo<\/code>: The logo image used for the print button.<\/li>\n<li><code>Map<\/code>: The map component that will be captured.<\/li>\n<li><code>useState<\/code>, <code>useRef<\/code>, <code>useCallback<\/code>: React hooks for state management, references, and callbacks.<\/li>\n<li><code>toPng<\/code>: A function from <code>html-to-image<\/code> to convert HTML elements to PNG images.<\/li>\n<\/ul>\n<h4>2. <strong>Initialize State and References<\/strong><\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-title function_\">PrintLayer<\/span> = () =&gt; {<br \/>\n<span class=\"hljs-keyword\">const<\/span> [hideComponents, setHideComponents] = <span class=\"hljs-title function_\">useState<\/span>(<span class=\"hljs-literal\">false<\/span>);<br \/>\n<span class=\"hljs-keyword\">const<\/span> [isLoading, setIsLoading] = <span class=\"hljs-title function_\">useState<\/span>(<span class=\"hljs-literal\">false<\/span>);<br \/>\n<span class=\"hljs-keyword\">const<\/span> mapRef = <span class=\"hljs-title function_\">useRef<\/span>();<br \/>\n<span class=\"hljs-keyword\">const<\/span> setMapRef = <span class=\"hljs-title function_\">useCallback<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">node<\/span>) =&gt;<\/span> {<br \/>\n<span class=\"hljs-keyword\">if<\/span> (node !== <span class=\"hljs-literal\">null<\/span>) {<br \/>\nmapRef.<span class=\"hljs-property\">current<\/span> = node;<br \/>\n}<br \/>\n}, []);<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li><code>hideComponents<\/code>: State to hide elements before capturing.<\/li>\n<li><code>isLoading<\/code>: State to show a loading message during the capture process.<\/li>\n<li><code>mapRef<\/code>: A reference for the map element that will be captured.<\/li>\n<li><code>setMapRef<\/code>: A callback to set the map reference.<\/li>\n<\/ul>\n<h4>3. <strong>Function to Capture Image<\/strong><\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-title function_\">handleCapture<\/span> = () =&gt; {<br \/>\n<span class=\"hljs-title function_\">setHideComponents<\/span>(<span class=\"hljs-literal\">true<\/span>); <span class=\"hljs-comment\">\/\/ Hide elements before capture<\/span><br \/>\n<span class=\"hljs-title function_\">setIsLoading<\/span>(<span class=\"hljs-literal\">true<\/span>); <span class=\"hljs-comment\">\/\/ Show loading message<\/span><br \/>\n<span class=\"hljs-built_in\">setTimeout<\/span>(<span class=\"hljs-function\">() =&gt;<\/span> {<br \/>\n<span class=\"hljs-keyword\">if<\/span> (mapRef.<span class=\"hljs-property\">current<\/span> === <span class=\"hljs-literal\">null<\/span>) {<br \/>\n<span class=\"hljs-keyword\">return<\/span>;<br \/>\n}<br \/>\n<span class=\"hljs-comment\">\/\/ Hide loading message before capture<\/span><br \/>\n<span class=\"hljs-title function_\">setIsLoading<\/span>(<span class=\"hljs-literal\">false<\/span>);<br \/>\n<span class=\"hljs-built_in\">setTimeout<\/span>(<span class=\"hljs-function\">() =&gt;<\/span> {<br \/>\n<span class=\"hljs-title function_\">toPng<\/span>(mapRef.<span class=\"hljs-property\">current<\/span>)<br \/>\n.<span class=\"hljs-title function_\">then<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">dataUrl<\/span>) =&gt;<\/span> {<br \/>\n<span class=\"hljs-comment\">\/\/ Save as image<\/span><br \/>\n<span class=\"hljs-keyword\">const<\/span> link = <span class=\"hljs-variable language_\">document<\/span>.<span class=\"hljs-title function_\">createElement<\/span>(<span class=\"hljs-string\">\"a\"<\/span>);<br \/>\nlink.<span class=\"hljs-property\">href<\/span> = dataUrl;<br \/>\nlink.<span class=\"hljs-property\">download<\/span> = <span class=\"hljs-string\">\"map.png\"<\/span>;<br \/>\nlink.<span class=\"hljs-title function_\">click<\/span>();<br \/>\n<span class=\"hljs-title function_\">setHideComponents<\/span>(<span class=\"hljs-literal\">false<\/span>); <span class=\"hljs-comment\">\/\/ Show elements after capture<\/span><br \/>\n})<br \/>\n.<span class=\"hljs-title function_\">catch<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">error<\/span>) =&gt;<\/span> {<br \/>\n<span class=\"hljs-variable language_\">console<\/span>.<span class=\"hljs-title function_\">error<\/span>(<span class=\"hljs-string\">\"Oops, something went wrong!\"<\/span>, error);<br \/>\n<span class=\"hljs-title function_\">setHideComponents<\/span>(<span class=\"hljs-literal\">false<\/span>); <span class=\"hljs-comment\">\/\/ Show elements after an error<\/span><br \/>\n});<br \/>\n}, <span class=\"hljs-number\">100<\/span>); <span class=\"hljs-comment\">\/\/ Short delay to ensure loading message is hidden<\/span><br \/>\n}, <span class=\"hljs-number\">1000<\/span>); <span class=\"hljs-comment\">\/\/ Delay to ensure components are hidden<\/span><br \/>\n};<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li><code>handleCapture<\/code>: Function to hide elements, show the loading message, and capture the map image. The image is then saved as a PNG file and downloaded.<\/li>\n<\/ul>\n<h4>4. <strong>Function to Trigger Capture<\/strong><\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-title function_\">toggleForm<\/span> = () =&gt; {<br \/>\n<span class=\"hljs-title function_\">handleCapture<\/span>(); <span class=\"hljs-comment\">\/\/ Immediately capture when the button is clicked<\/span><br \/>\n};<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li><code>toggleForm<\/code>: Function that calls <code>handleCapture<\/code> when the print button is clicked.<\/li>\n<\/ul>\n<h4>5. <strong>Render the PrintLayer Component<\/strong><\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">return<\/span> (<br \/>\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span><br \/>\n<span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span><br \/>\n<span class=\"hljs-attr\">position:<\/span> \"<span class=\"hljs-attr\">absolute<\/span>\",<br \/>\n<span class=\"hljs-attr\">top:<\/span> \"<span class=\"hljs-attr\">10<\/span>%\",<br \/>\n<span class=\"hljs-attr\">right:<\/span> \"<span class=\"hljs-attr\">28px<\/span>\",<br \/>\n<span class=\"hljs-attr\">border:<\/span> \"<span class=\"hljs-attr\">3px<\/span> <span class=\"hljs-attr\">solid<\/span> <span class=\"hljs-attr\">white<\/span>\",<br \/>\n<span class=\"hljs-attr\">background:<\/span> \"<span class=\"hljs-attr\">rgba<\/span>(<span class=\"hljs-attr\">255<\/span>, <span class=\"hljs-attr\">255<\/span>, <span class=\"hljs-attr\">255<\/span>)\",<br \/>\n<span class=\"hljs-attr\">padding:<\/span> \"<span class=\"hljs-attr\">20px<\/span>\",<br \/>\n<span class=\"hljs-attr\">borderRadius:<\/span> \"<span class=\"hljs-attr\">100px<\/span>\",<br \/>\n<span class=\"hljs-attr\">zIndex:<\/span> <span class=\"hljs-attr\">1000<\/span>,<br \/>\n<span class=\"hljs-attr\">backgroundImage:<\/span> `<span class=\"hljs-attr\">url<\/span>(${<span class=\"hljs-attr\">logo<\/span>})`,<br \/>\n<span class=\"hljs-attr\">backgroundRepeat:<\/span> \"<span class=\"hljs-attr\">no-repeat<\/span>\",<br \/>\n<span class=\"hljs-attr\">backgroundSize:<\/span> \"<span class=\"hljs-attr\">30px<\/span>\",<br \/>\n<span class=\"hljs-attr\">backgroundPosition:<\/span> \"<span class=\"hljs-attr\">center<\/span>\",<br \/>\n<span class=\"hljs-attr\">cursor:<\/span> \"<span class=\"hljs-attr\">pointer<\/span>\",<br \/>\n<span class=\"hljs-attr\">transition:<\/span> \"<span class=\"hljs-attr\">transform<\/span> <span class=\"hljs-attr\">0.3s<\/span> <span class=\"hljs-attr\">ease-out<\/span>\",<br \/>\n}}<br \/>\n<span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{toggleForm}<\/span><br \/>\n<span class=\"hljs-attr\">onMouseEnter<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> {<br \/>\ne.target.style.transform = \"scale(1.1)\";<br \/>\n}}<br \/>\nonMouseLeave={(e) =&gt; {<br \/>\ne.target.style.transform = \"scale(1)\";<br \/>\n}}<br \/>\n&gt;<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">ref<\/span>=<span class=\"hljs-string\">{setMapRef}<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">position:<\/span> \"<span class=\"hljs-attr\">relative<\/span>\" }}&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Map<\/span> <span class=\"hljs-attr\">hideComponents<\/span>=<span class=\"hljs-string\">{hideComponents}<\/span> \/&gt;<\/span><br \/>\n{isLoading &amp;&amp; (<br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span><br \/>\n<span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span><br \/>\n<span class=\"hljs-attr\">position:<\/span> \"<span class=\"hljs-attr\">absolute<\/span>\",<br \/>\n<span class=\"hljs-attr\">top:<\/span> \"<span class=\"hljs-attr\">50<\/span>%\",<br \/>\n<span class=\"hljs-attr\">left:<\/span> \"<span class=\"hljs-attr\">50<\/span>%\",<br \/>\n<span class=\"hljs-attr\">transform:<\/span> \"<span class=\"hljs-attr\">translate<\/span>(<span class=\"hljs-attr\">-50<\/span>%, <span class=\"hljs-attr\">-50<\/span>%)\",<br \/>\n<span class=\"hljs-attr\">background:<\/span> \"<span class=\"hljs-attr\">rgba<\/span>(<span class=\"hljs-attr\">255<\/span>, <span class=\"hljs-attr\">255<\/span>, <span class=\"hljs-attr\">255<\/span>, <span class=\"hljs-attr\">0.8<\/span>)\",<br \/>\n<span class=\"hljs-attr\">padding:<\/span> \"<span class=\"hljs-attr\">20px<\/span>\",<br \/>\n<span class=\"hljs-attr\">borderRadius:<\/span> \"<span class=\"hljs-attr\">8px<\/span>\",<br \/>\n<span class=\"hljs-attr\">zIndex:<\/span> <span class=\"hljs-attr\">1000<\/span>,<br \/>\n}}<br \/>\n&gt;<\/span><br \/>\nLoading...<br \/>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><br \/>\n)}<br \/>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span><br \/>\n);<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li>A print button positioned at the top-right corner with hover animation.<\/li>\n<li>The map area to be captured, with a loading message displayed during the capture process.<\/li>\n<\/ul>\n<h4>6. <strong>Export the Component<\/strong><\/h4>\n<div class=\"dark bg-gray-950 rounded-md border-[0.5px] border-token-border-medium\">\n<div class=\"flex items-center relative text-token-text-secondary bg-token-main-surface-secondary px-4 py-2 text-xs font-sans justify-between rounded-t-md\"><\/div>\n<div class=\"overflow-y-auto p-4\" dir=\"ltr\"><code class=\"!whitespace-pre hljs language-javascript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-title class_\">PrintLayer<\/span>;<br \/>\n<\/code><\/div>\n<\/div>\n<p><strong>Explanation:<\/strong><\/p>\n<ul>\n<li>Exporting the <code>PrintLayer<\/code> component so it can be used elsewhere in the application.<\/li>\n<\/ul>\n<hr \/>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/article>\n<article class=\"w-full text-token-text-primary focus-visible:outline-2 focus-visible:outline-offset-[-4px]\" dir=\"auto\" data-testid=\"conversation-turn-15\" data-scroll-anchor=\"false\">\n<div class=\"text-base py-[18px] px-3 md:px-4 m-auto md:px-5 lg:px-1 xl:px-5\">\n<div class=\"mx-auto flex flex-1 gap-4 text-base md:gap-5 lg:gap-6 md:max-w-3xl lg:max-w-[40rem] xl:max-w-[48rem]\">\n<div class=\"group\/conversation-turn relative flex w-full min-w-0 flex-col agent-turn\">\n<div class=\"flex-col gap-1 md:gap-3\">\n<div class=\"flex max-w-full flex-col flex-grow\">\n<div class=\"min-h-[20px] text-message flex w-full flex-col items-end gap-2 whitespace-pre-wrap break-words [.text-message+&amp;]:mt-5 overflow-x-auto\" dir=\"auto\" data-message-author-role=\"assistant\" data-message-id=\"f78e097b-0db7-460b-b365-86de4908b0b0\">\n<div class=\"flex w-full flex-col gap-1 empty:hidden first:pt-[3px]\">\n<div class=\"markdown prose w-full break-words dark:prose-invert dark\">\n<p>&nbsp;<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/article>\n<hr \/>\n","protected":false},"excerpt":{"rendered":"<p>Tutorial: Implementing a Search Feature with React and Leaflet In this tutorial, we will build a Search component that allows users to search for locations and display the results on a map with a popup containing location information. We will use React, React-Leaflet, and leaflet-geosearch for this component. 1. Import Required Libraries import React, { [&hellip;]<\/p>\n","protected":false},"author":46,"featured_media":179,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-154","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-frontend"],"_links":{"self":[{"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/posts\/154","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/users\/46"}],"replies":[{"embeddable":true,"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/comments?post=154"}],"version-history":[{"count":10,"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/posts\/154\/revisions"}],"predecessor-version":[{"id":168,"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/posts\/154\/revisions\/168"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/media\/179"}],"wp:attachment":[{"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/media?parent=154"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/categories?post=154"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/research.binus.ac.id\/geoecoai\/wp-json\/wp\/v2\/tags?post=154"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}