<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Harel Mazor on Medium]]></title>
        <description><![CDATA[Stories by Harel Mazor on Medium]]></description>
        <link>https://medium.com/@harel.mazor?source=rss-f6564581741d------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*NyvehqGTVl0THLl_iG_Z6Q.png</url>
            <title>Stories by Harel Mazor on Medium</title>
            <link>https://medium.com/@harel.mazor?source=rss-f6564581741d------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 07 Apr 2026 23:47:03 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@harel.mazor/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How to Use patch-package to Fix or Customize a Capacitor Plugin]]></title>
            <link>https://medium.com/@harel.mazor/how-to-use-patch-package-to-fix-or-customize-a-capacitor-plugin-5885c50c90cd?source=rss-f6564581741d------2</link>
            <guid isPermaLink="false">https://medium.com/p/5885c50c90cd</guid>
            <category><![CDATA[patch-package]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[capacitor]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[patch]]></category>
            <dc:creator><![CDATA[Harel Mazor]]></dc:creator>
            <pubDate>Mon, 21 Jul 2025 09:12:27 GMT</pubDate>
            <atom:updated>2025-07-21T09:12:27.644Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*AYtfV6brttIEG9e3" /><figcaption>Photo by <a href="https://unsplash.com/@vladimir_d?utm_source=medium&amp;utm_medium=referral">Volodymyr Dobrovolskyy</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>If you’ve ever hit a wall with a Capacitor plugin — maybe a bug in native code or a missing feature — you know how frustrating it can be to wait on a pull request or maintain your own fork. Luckily, there’s a better way: patch-package.</p><p>This post walks you through how to safely patch Capacitor plugins using patch-package, so you can fix or tweak plugins without forking them — and keep those changes consistent across environments and installs.</p><h3>What Is patch-package?</h3><p>patch-package is a tool that lets you make changes directly inside node_modules and preserve those changes across reinstalls.</p><p>Instead of forking a dependency, you:</p><ul><li>Make the changes you need</li><li>Run a command to generate a .patch file.</li><li>Let patch-package reapply your changes automatically during install or build</li></ul><p>It’s an especially handy tool for Capacitor developers (or other npm users), where plugin issues can often involve native Android or iOS code that’s not easily overridden without modifying the source.</p><h3>How Does patch-package Work?</h3><p>When I first found out how it works I was just amazed by its simplicity and powerfulness, so I thought it deserves a paragraph in my blog post.</p><p>Let’s demystify it. when you run npx patch-package some-package, it:</p><p>1. Compares the original package (from node_modules) to your modified version.</p><p>2. Generates a .patch file (a Git-style diff) that captures those changes.</p><p>3. Stores the patch in a /patches folder in your project.</p><p>4. Applies the patch automatically via a script (we’ll use npm&#39;sprepare hook) every time dependencies are installed or the app is built.</p><p>This means you get the exact changes you made — even on CI servers or other team members’ machines — without touching the actual package repo or forking it.</p><p>Example patch file name:</p><p>patches/@capacitor+camera+5.0.3.patch</p><p>That file will only apply to version 5.0.3 of the @capacitor/camera plugin.</p><h3>Why You Might Need It in a Capacitor Project?</h3><p>Capacitor plugins are unique: they often include native code (Java, Kotlin, Swift, Obj-C) and a JS wrapper. This makes customization hard without digging into internals.</p><p>You might use patch-package to:</p><p>Fix a bug in Android or iOS code (e.g., permission handling)</p><p>Modify a JS wrapper to expose more native functionality</p><p>Remove hardcoded behavior that conflicts with your app</p><p>Instead of maintaining a fork or waiting for maintainers to respond to an issue or PR, you can patch it immediately.</p><p>More over, you can patch it while waiting for the PR to be approved and then get rid of the patch once a new version with your fix is released.</p><h3>Step-by-Step: Patching a Capacitor Plugin</h3><ol><li>Install patch-package</li></ol><p>npm i -D patch-package</p><p>2. Then update your package.json:</p><pre>&quot;scripts&quot;: {<br>    ...<br>    &quot;prepare&quot;: &quot;patch-package&quot;<br>}</pre><p>The prepare script runs automatically after npm install and before builds or publishes. It ensures patches are applied reliably across all environments without bloating the install process itself.</p><p>3. Make the Changes You Need</p><p>Find the plugin you want to modify inside node_modules. For example, if you’re patching the Capacitor Camera plugin:</p><p>node_modules/@capacitor/camera/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.java</p><p>Make your edits directly to the source file(s). This could be:</p><ul><li>Modifying native code behavior</li><li>Fixing an error</li><li>Changing the way JS interacts with native layers</li></ul><p>4. Generate the Patch</p><p>Once you’re happy with your changes, run:</p><p>npx patch-package @capacitor/camera</p><p>This creates a file in the patches/ folder:</p><p>patches/@capacitor+camera+5.0.3.patch</p><p>Open the patch file and you’ll see a Git-style diff with your changes.</p><p>5. Test and commit</p><p>Run your app to make sure everything works as expected. Then commit the patch file and updated scripts.</p><p>Now any time your app is installed or deployed, the patch will be reapplied automatically.</p><h3>Things to Watch Out For</h3><p>Version sensitivity: Patches only apply to the exact version you generated them from. Upgrading the plugin may require redoing the patch.</p><p>Conflicts: If the structure of the plugin changes upstream, the patch may fail to apply. You’ll get a clear error if this happens.</p><p>Not for large rewrites: patch-package is great for small or temporary fixes. For larger long-term changes, consider forking the plugin.</p><p>Make sure the diff is clean by removing any files in the node_modules folder of the package that are not needed (like the build folder in android for example)</p><h3>Bonus: Patching Native Code? Yes, You Can!</h3><p>Because patch-package works on files inside node_modules, you can use it to patch native Android or iOS code — something not easily done otherwise.</p><p>Just remember to run:</p><p>npx cap sync</p><p>after making changes to native code so Capacitor rebuilds with the latest.</p><h3>Final Thoughts</h3><p>Using patch-package with Capacitor plugins is a lightweight, efficient way to fix bugs or make critical tweaks without losing your mind over forks or upstream delays. It’s also relevant to other npm packages but since TypeScript and JavaScript packages may require an additional steps of cloning and building, the Capacitor example is used in this article, since it’s a lot simpler.</p><p>It’s perfect for teams that:</p><ul><li>Need to move fast</li><li>Want reproducible fixes across all environments</li><li>Don’t want to fork every time a plugin needs a minor change</li></ul><p>And with the prepare script in place, your patches stay rock solid from dev to CI to production.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5885c50c90cd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sharing Code Across C++, TypeScript, and Rust at MapLibre: A POC and Its Lessons]]></title>
            <link>https://medium.com/@harel.mazor/sharing-code-across-c-typescript-and-rust-at-maplibre-a-poc-and-its-lessons-ce2a6d9cfaa8?source=rss-f6564581741d------2</link>
            <guid isPermaLink="false">https://medium.com/p/ce2a6d9cfaa8</guid>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[maplibre]]></category>
            <category><![CDATA[typescirpt]]></category>
            <category><![CDATA[cpp]]></category>
            <category><![CDATA[wasm]]></category>
            <dc:creator><![CDATA[Harel Mazor]]></dc:creator>
            <pubDate>Sat, 28 Jun 2025 11:39:52 GMT</pubDate>
            <atom:updated>2025-06-28T11:41:29.025Z</atom:updated>
            <content:encoded><![CDATA[<p>At MapLibre, we’re always looking for ways to push the boundaries of open-source mapping on the web. One recurring question in our work is: <em>How can we share code between our web and native implementations?</em></p><p>Recently, I explored this question by building a proof of concept (POC) to share vector tile slicing logic across platforms. Here’s what I learned.</p><h3>The Motivation: One Core, Many Targets</h3><p>maplibre-native is written in C++ and recently was intoduced to a Rust library for color parsing. Meanwhile, maplibre-gl-js powers the browser side. Maintaining separate implementations for the same logic means duplicated effort and potential inconsistencies.</p><p>Sharing code would:</p><ul><li>Reduce bugs due to inconsistent behavior.</li><li>Lower maintenance cost.</li><li>Ensure performance optimizations benefit all platforms.</li></ul><h3>The POC: Using Rust and geojson-vt-rs</h3><p>To test this, I tried to integrate a library that’s a bit bigger than color parsing called <a href="https://github.com/harelm/geojson-vt-rs">geojson-vt-rs</a>, a Rust implementation of the vector tile slicing algorithm that already exists in both c++ and JavaScript. Rust is a natural fit for this because:</p><ul><li>It’s performant like C++.</li><li>It has great WebAssembly (Wasm) support, making it easier to run in the browser.</li><li>Its ecosystem for cross-compiling is mature.</li></ul><p>The idea was to compile the Rust code to WebAssembly, then import it into the maplibre-gl-js TypeScript project.</p><p>I must admit that getting it to create a wasm with some javascript wrapping code and typescript definition was the easier part of all this POC.</p><p>I added some wrapping impl of the struct I needed to export with some JsValue input and output parameters, #[wasm_bindgen] annotations and used the amazing wasm-pack and tool to built it:</p><p>wasm-pack build --target web</p><p>This created a few files that allows to create an npm package with the following command:</p><p>wasm-pack pack</p><p>Unfortunately, it didn’t work out of the box and I did need to edit two files to make it work.</p><ol><li>Remove the following lines from geojson_vt_rs.js as they created some issue with the (complicated) rollup setup we have.</li></ol><pre>- if (typeof module_or_path === &#39;undefined&#39;) {<br>-    module_or_path = new URL(&#39;geojson_vt_rs_bg.wasm&#39;, import.meta.url);<br>- }</pre><p>2. export the wasm file by editing package.json file.</p><p>Once the above were done I could create an npm package using npm pack command and install this locally created file in maplibre-gl-js project.</p><p>In this next part I needed to use the @rollup/plugin-wasm in order to inline the wasm into the JavaScript code with the wasm({targetEnv:”auto-inline”}) and also import and initialize it in the section of the code that I wanted to change:</p><pre>// import the relevant code<br>import init, {GeoJSONVT} from &#39;geojson-vt-rs&#39;;<br>// @ts-ignore<br>import wasm from &#39;geojson-vt-rs/geojson_vt_rs_bg.wasm&#39;;<br><br>...<br><br>// initalizing the wasm, should be done once, <br>// !! this is important for rollup to pick it up !!<br>await init(wasm());<br><br>...<br>// using the wasm code<br>this._geoJSONIndex = GeoJSONVT.from_jsobjects(pendingData) as any;</pre><h3>The Good: It Works!</h3><p>Technically, the experiment worked! The Rust code compiled to Wasm, and the TypeScript project could use it for slicing GeoJSON into vector tiles on the fly. The output matched expectations and proved that sharing low-level logic across languages is feasible.</p><h3>The Bad: Package Size Woes</h3><p>However, there was a big catch: <strong>package size</strong>.</p><p>The Wasm bundle added significant weight to the final JavaScript bundle (around 30%). For modern web apps, where every kilobyte affects performance and time-to-first-paint, this was unacceptable. The tradeoff didn’t make sense for MapLibre’s goal of delivering fast and lightweight maps.</p><h3>Next Steps</h3><p>For now, we’ll continue to use the current implementations for every platform. An idea I had was to bundle all the shared code in a single Rust generated wasm in order to remove some of the overhead related to code sharing, the downside of this idea is that you can’t split the library into smaller pieces to allow tree-shaking in the future as these two idea kinda collide.</p><p>Sharing code across C++, TypeScript, and Rust is an exciting frontier for projects like MapLibre. If you’ve experimented with this or have ideas for keeping Wasm bundles slim, I’d love to hear from you!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ce2a6d9cfaa8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[To chainable or not to chainable]]></title>
            <link>https://medium.com/@harel.mazor/to-chainable-or-not-to-chainable-ba7bfd59586c?source=rss-f6564581741d------2</link>
            <guid isPermaLink="false">https://medium.com/p/ba7bfd59586c</guid>
            <category><![CDATA[fluent]]></category>
            <category><![CDATA[api]]></category>
            <category><![CDATA[backwards-compatibility]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[chainable]]></category>
            <dc:creator><![CDATA[Harel Mazor]]></dc:creator>
            <pubDate>Tue, 08 Oct 2024 20:44:54 GMT</pubDate>
            <atom:updated>2024-10-08T20:44:54.873Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JQwCa7tns0XhJGDf" /><figcaption>Photo by <a href="https://unsplash.com/@mikezo?utm_source=medium&amp;utm_medium=referral">Mike Alonzo</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>The history of chainable is pretty long, by now this is far from a new concept. I first encountered this back in the days when I wrote in C# and <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/">LINQ</a> was a shiny new addition to the language.</p><h3>What is chainable?</h3><p>If you are already familiar with the concept you can skip right ahead to what I think about in the next paragraph, if not, the definition is pretty simple: The ability to chain a few methods one after the other.</p><p>for example:</p><pre>let a = new A();<br>a.setSomething(&quot;something&quot;).setSomethingElse(2).doSomething();</pre><p>The implementation of this is fairly simple, just return the class this method belongs to in every method and you can chain methods one after the other.</p><h3>Ok, great! I want two to go please!</h3><p>This is great for some use cases, but not a great for other.</p><p>Builder pattern is the most know example of a good usage of chainable — it creates an immutable object, and every method returns a new immutable object, so that you can’t modify an object but only create a new one from an existing on, which guards the immutability of an object.</p><p>Another good example is stream manipulation or array manipulation, like was introduced in LINQ, here’s a similar example in javascript:</p><pre>let a = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;];<br>let b = a.map(item =&gt; { return { num: 1, text: item }}).filter(item =&gt; item.num &gt; 2);</pre><h3>So, what’s the catch?</h3><p>The problem with chainable comes from the definition I wrote earlier — “just return the class this method belongs to in every method…”. This is problematic for a few reasons.</p><p>The first reason is inheritance, returning the base object for inherited classes can create an awkward code, but you might be dismiss this by saying “don’t inherit, use composition”, which I generally agree so let’s say this is a “weak” argument.</p><p>The main reason for me, and probably why I decided to write this blog post, is backward API compatibility issues for methods that in theory should’ve return void and now return this which changing them will break existing code.</p><p>Let’s take a look at a real problem I faced while maintaining one my open source projects. In this project, which I inherited, most of the methods were chainable, including the following one:</p><pre>class Map {<br>  ...<br>  // this method allows regitering for an event with the name &quot;type&quot;.<br>  // It returns &quot;this&quot; to allow chaining<br>  on(type: string, listerner: Listener): this {<br>    ...<br>    return this;<br>  }<br>}</pre><p>So the code to use this would be something like:</p><pre>let map = new Map();<br>map.on(&#39;move&#39;, () =&gt; { doSomething() });</pre><p>Great! I can register to a move event and execute my code when this events gets fired. In some cases I would like to unregister from the move event and stop receiving notification about it. In that case I would use the off method, but I need to change my code now in order to achieve that into something like:</p><pre>let map = new Map();<br>let listener = () =&gt; { doSomething() };<br>map.on(&#39;move&#39;, listener);<br>//...<br>map.off(&#39;move&#39;, listener);</pre><p>That’s not very pretty, and RxJS already showed us that there’s a more elegant way to achieve this using subscription, right?</p><p>So if the on method would return something that I can call in order to unsubscribe I could hold onto that and call this unsubscribe later on in my code, something like:</p><pre>let map = new Map();<br>let subscription = map.on(&#39;move&#39;, () =&gt; { doSomething() });<br>//...<br>subscription.unsubscribe();</pre><p>This is a lot more elegant, and a lot more modern, but here’s the catch: I can’t change the return value of the method because it is chainable, and chainging that will create a breaking change to the API and users who depend on the chainable functionality will need to change their code.</p><p>In this case and in a lot of other cases, I’ve seen places that use a chainable API that can create maintenance problems of keeping an API backwards compatible. Chainable has merit, but in most cases that’s I’ve seen it saves a few clicks on the keyboard of developers at best.</p><h3>My advice</h3><p>Don’t force a chainable API where it’s not necessary, your future self will thank you 😊</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ba7bfd59586c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bad error message and wrong defaults creates the worst user experience!]]></title>
            <link>https://medium.com/@harel.mazor/bad-error-message-and-wrong-defaults-creates-the-worst-user-experience-37e0b576daa3?source=rss-f6564581741d------2</link>
            <guid isPermaLink="false">https://medium.com/p/37e0b576daa3</guid>
            <category><![CDATA[user-experience]]></category>
            <category><![CDATA[ux]]></category>
            <category><![CDATA[error-message]]></category>
            <category><![CDATA[default]]></category>
            <dc:creator><![CDATA[Harel Mazor]]></dc:creator>
            <pubDate>Mon, 13 Nov 2023 09:57:07 GMT</pubDate>
            <atom:updated>2023-11-13T09:57:07.754Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*QAqR8y0gFo671cqX" /><figcaption>Photo by David Pupăză on Unsplash</figcaption></figure><p>When it comes to user experience, there are a lot of places you can create a positive and fun user experience, but also a lot of places where you can really frustrate the user…</p><p>You can do animations, graphics and some really nice stuff, but the place where most of the apps lack is when things don’t go as expected. The default is usually an unhelpful message like “oops, please try again later”, and when you try again, it doesn’t help, surprisingly…</p><p>In the past few days, I have encountered a situation where bad error message and wrong defaults left me, a very seasoned developer, with trial and error to understand how to overcome a problem.</p><p>This is my story, hoping anyone who will read this will later on remember it when returning an unhelpful error message or forgetting to set the right defaults.</p><p>Here goes. I made a few changes to my library including some CI improvements and wanted to publish a new version to npm, so I used the CI tools to do that and got the following error message:</p><pre>npm ERR! code ENEEDAUTH<br>npm ERR! need auth This command requires you to be logged in to https://registry.npmjs.org/<br>npm ERR! need auth You need to authorize this machine using `npm adduser`<br></pre><p>The error message itself seems like a login is needed, which was weird since I didn’t change anything related to my npm token.</p><p>I thought it was something I changed related to “who” runs the CI pipeline, so I started to revert stuff, but nothing helped.</p><p>After a few hours of reading GitHub issues, I found someone saying that I need to configure my registry-url, which I accidetally removed in one of my commits, apparently.</p><p>I then remembered that I have changed the setup-node configuration of my CI to use my .nvmrc file so that everything will use the same node version, both for development and for CI, little did I know that this change will cause a npm publish failure…</p><p>For reference here’s the relevant commit change of my CI:</p><pre>      - uses: actions/setup-node@v3<br>        with:<br>-         node-version: lts/*<br>+         node-version-file: &#39;.nvmrc&#39;</pre><p>So, I added the relevant parameter and the publish started working again! Yay!</p><p>In the above example you can easily see that if the defaults were better, e.g. when not setting registry-url it will use the default npm registry, this issue could have been avoided. A better error message like “registry is not defined” would have allow me to fix the issue a lot faster and lead me in the right path.</p><p>I’m hoping you could keep this story in mind the next time you return an error message or define a default 😀</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=37e0b576daa3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why I never use “get” and “set” in any coding language]]></title>
            <link>https://medium.com/@harel.mazor/why-i-never-use-get-and-set-in-any-code-language-7a0f7de739ce?source=rss-f6564581741d------2</link>
            <guid isPermaLink="false">https://medium.com/p/7a0f7de739ce</guid>
            <category><![CDATA[clean-code]]></category>
            <category><![CDATA[getters-and-setters]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[best-practices]]></category>
            <dc:creator><![CDATA[Harel Mazor]]></dc:creator>
            <pubDate>Thu, 19 Oct 2023 06:48:15 GMT</pubDate>
            <atom:updated>2023-10-19T07:00:00.353Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*vCk71sqOJFsGEGz8" /><figcaption>Photo by <a href="https://unsplash.com/@nxvision?utm_source=medium&amp;utm_medium=referral">Nigel Tadyanehondo</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h4>A little story to explain why I hate getters and setters.</h4><p>I encountered the following seamlessly naive piece of code:</p><pre>apply(that: Transform) {<br>  this.tileSize = that.tileSize;<br>  this.latRange = that.latRange;<br>  this.width = that.width;<br>  this.height = that.height;<br>  this._center = that._center;<br>  this._elevation = that._elevation;<br>  this._minEleveationForCurrentTile = that._minEleveationForCurrentTile;<br>  this.zoom = that.zoom;<br>  this.angle = that.angle;<br>  this._fov = that._fov;<br>  this._pitch = that._pitch;<br>  this._unmodified = that._unmodified;<br>  this._edgeInsets = that._edgeInsets.clone();<br>  this._calcMatrices();<br>}</pre><p>This method is not very interesting, it basically applies a Transform onto this which is of type Transform and updates its internal members.</p><p>When reading this code, it was obvious to me that this._calcMetrices() is an operation that takes time and should be done only after setting all the values of this.</p><p>Little did I know that when setting the value of what appeared to be an innocent property zoom the following is what is being executed:</p><pre>get zoom(): number { return this._zoom; }<br>set zoom(zoom: number) {<br>    const constrainedZoom = Math.min(Math.max(zoom, this.minZoom), this.maxZoom);<br>    if (this._zoom === constrainedZoom) return;<br>    this._unmodified = false;<br>    this._zoom = constrainedZoom;<br>    this.tileZoom = Math.max(0, Math.floor(constrainedZoom));<br>    this.scale = this.zoomScale(constrainedZoom);<br>    this._constrain();<br>    this._calcMatrices();<br>}</pre><p>It took me a few hours to understand that this is the cause of what appeared to be a bug in a new code I introduced.</p><p>It is also obvious that this._calcMatrices() is not called at the end of the first method and it’s called more than once!</p><p>This is not the first and probably not the last time I’m experiencing this kind of frustration from code that uses get and set.</p><p>So, my suggestion to anyone reading this: <strong>Stop using those!</strong></p><p>You can easily write the following, more readable code:</p><pre>setZoomAndUpdateState(zoom: number) {<br>    const constrainedZoom = Math.min(Math.max(zoom, this.minZoom), this.maxZoom);<br>    if (this._zoom === constrainedZoom) return;<br>    this._unmodified = false;<br>    this._zoom = constrainedZoom;<br>    this.tileZoom = Math.max(0, Math.floor(constrainedZoom));<br>    this.scale = this.zoomScale(constrainedZoom);<br>    this._constrain();<br>    this._calcMatrices();<br>}</pre><p>It will achieve the same result and will be a lot more readable in the initial discussed method, here’s how it would look:</p><pre>apply(that: Transform) {<br>  this.tileSize = that.tileSize;<br>  this.latRange = that.latRange;<br>  this.width = that.width;<br>  this.height = that.height;<br>  this._center = that._center;<br>  this._elevation = that._elevation;<br>  this._minEleveationForCurrentTile = that._minEleveationForCurrentTile;<br>  this.setZoomAndUpdateState(that.zoom);<br>  this.angle = that.angle;<br>  this._fov = that._fov;<br>  this._pitch = that._pitch;<br>  this._unmodified = that._unmodified;<br>  this._edgeInsets = that._edgeInsets.clone();<br>  this._calcMatrices();<br>}</pre><h3>Happy coding!</h3><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7a0f7de739ce" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using the great power of addProtocol with MapLibre]]></title>
            <link>https://medium.com/@harel.mazor/using-the-great-power-of-addprotocol-with-maplibre-29dc612fa29a?source=rss-f6564581741d------2</link>
            <guid isPermaLink="false">https://medium.com/p/29dc612fa29a</guid>
            <category><![CDATA[maplibre]]></category>
            <category><![CDATA[maps]]></category>
            <dc:creator><![CDATA[Harel Mazor]]></dc:creator>
            <pubDate>Tue, 13 Dec 2022 08:56:28 GMT</pubDate>
            <atom:updated>2022-12-13T09:02:19.302Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>With great power comes great responsibility, Uncle Ben</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*5vmOh6tuaFPC4c2g" /><figcaption>A map photo by <a href="https://unsplash.com/@tjump?utm_source=medium&amp;utm_medium=referral">Nik Shuliahin 💛💙</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>addProtocol is one of the most powerful capabilities that were added to one of the earlier versions of <a href="https://www.npmjs.com/package/maplibre-gl">maplibre-gl-js</a>.</p><p>addProtocol basically adds a callback to be called before any ajax request made by the library, which allow intercepting the request, changing the data and returning it for continued processing and rendering.</p><p>Let’s see some javascript code:</p><pre>import maplibregl from &#39;maplibre-gl-js&#39;<br><br>maplibre.addProtocol(&#39;my-protocol&#39;, (params, callback) =&gt; {<br>    const geojson = { type: &#39;Feature&#39;, geometry: {...} };<br>    callback(null, geojson, null, null);<br>    return { cancel: () =&gt; { } };<br>});<br><br>const map = new maplibregl.Map({<br>  container: &#39;map&#39;,<br>    style: {<br>      version: 8,<br>      sources: {<br>        customSource: {<br>          type: &#39;geojson&#39;,<br>          data: &#39;my-protocol://data.json&#39;<br>        }<br>      },<br>      layers: [{<br>        id: &#39;layer&#39;<br>        type: &#39;circle&#39;<br>      }]<br>  }<br>});<br></pre><p>In the above example we are using a custom protocol: my-protocol and when MapLibre is trying to fetch the data at my-protocol://data.json the code we registered is called and we can, in this case, return a geojson object which will be processed and presented.</p><p>Note that the type of the source is important in order to return the right data — for geojson you need to return a geojson object, for raster you need to return arrayBuffer of the image, and for mvt the content the pbf in arrayBuffer format.</p><p>That’s it! Super simple, super powerful!</p><p>Here’s an example of how it can look (given the right geojson object):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/670/1*NVloshwNHL8r-xjEU6Y36g.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=29dc612fa29a" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>