<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hyster Alan</title>
    <description>The latest articles on DEV Community by Hyster Alan (@hyster_alan_cae6913e040c6).</description>
    <link>https://dev.to/hyster_alan_cae6913e040c6</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1665096%2F99c75728-4da7-44b6-9add-d7f5a8bdbc2c.jpg</url>
      <title>DEV Community: Hyster Alan</title>
      <link>https://dev.to/hyster_alan_cae6913e040c6</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hyster_alan_cae6913e040c6"/>
    <language>en</language>
    <item>
      <title>TerraSight Offline Satellite Analysis Powered by Gemma 4</title>
      <dc:creator>Hyster Alan</dc:creator>
      <pubDate>Sun, 24 May 2026 06:30:54 +0000</pubDate>
      <link>https://dev.to/hyster_alan_cae6913e040c6/terrasight-offline-satellite-analysis-powered-by-gemma-4-2nkk</link>
      <guid>https://dev.to/hyster_alan_cae6913e040c6/terrasight-offline-satellite-analysis-powered-by-gemma-4-2nkk</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-gemma-2026-05-06"&gt;Gemma 4 Challenge: Build with Gemma 4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;TerraSight is a geospatial analysis tool that runs entirely on your machine. You upload raw Landsat 9 satellite band files, draw a study boundary, and the app computes three indices: NDVI (vegetation health), NDBI (urban surface coverage), and LST (land surface temperature), then renders calibrated colour maps and lets you interrogate the results through an AI chat interface.&lt;/p&gt;

&lt;p&gt;No cloud processing. No API keys. No data leaving your machine.&lt;/p&gt;

&lt;p&gt;The problem I was solving is straightforward: satellite imagery analysis has always sat behind expensive software licenses (ENVI, ArcGIS) or required GIS expertise most people don't have. Farmers checking crop stress, students doing fieldwork, community researchers tracking urban heat, they don't need a commercial GIS suite. They need something that takes a file, does the math correctly, and explains what the numbers mean in plain language.&lt;/p&gt;

&lt;p&gt;That last part is where Gemma 4 comes in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/DgyzkZAl5fQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/HysterAlan1/terrasight-geoai" rel="noopener noreferrer"&gt;github.com/HysterAlan1/terrasight-geoai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project is split into two files by design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gis_engine.py&lt;/code&gt; — all geospatial computation, radiometric scaling, and AI logic. No Streamlit. You can import it directly into a notebook or script.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app.py&lt;/code&gt; — the Streamlit UI. It calls the engine, handles session state, and renders everything. Nothing that touches pixels lives here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The processing pipeline applies official USGS Collection 2 Level-2 scale factors to convert raw digital numbers into physical values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Optical bands → surface reflectance
&lt;/span&gt;&lt;span class="n"&gt;reflectance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;DN&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.0000275&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Thermal band → Celsius
&lt;/span&gt;&lt;span class="n"&gt;lst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DN&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.00341802&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;149.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;273.15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pixels where any optical band reads zero get masked to &lt;code&gt;NaN&lt;/code&gt; before any index is computed. This matters — skipping that step would quietly corrupt NDVI and NDBI values for no-data areas without raising any error.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Used Gemma 4
&lt;/h2&gt;

&lt;p&gt;I used &lt;strong&gt;Gemma 4 E4B&lt;/strong&gt; running locally via Ollama.&lt;/p&gt;

&lt;p&gt;The choice of E4B was deliberate. The use case is offline; the whole point of the tool is that your satellite data stays on your machine. E4B runs comfortably on consumer hardware without a GPU, which means it works on the kind of laptop a park ranger or agriculture student actually has. A larger model would have defeated the purpose.&lt;/p&gt;

&lt;p&gt;Gemma 4 handles two things in TerraSight:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Contextual interpretation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every time a user sends a message in the AI terminal, Gemma receives the actual computed statistics, not just the question, as part of its context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NDVI=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mean_ndvi&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, NDBI=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mean_ndbi&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, LST=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mean_lst&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;°C, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. User asks: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Answer concisely.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the model's answers are grounded in the real numbers from that specific scene, not generic satellite literacy. Ask it, "Why is the LST so high in the centre?" and it answers in terms of the actual value you computed, not a textbook definition of urban heat islands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Graceful degradation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I also built a keyword-based rule engine that fires when Ollama is unreachable. It interprets the three index values using threshold tables and routes the answer based on what the user appears to be asking. This means the tool remains fully functional without AI  Gemma, but the analysis doesn't break without it.&lt;/p&gt;

&lt;p&gt;What I found working with E4B: it handles domain-specific questions well when the numbers are injected directly. Vague prompts get vague answers. Specific prompts, "NDVI=0.21, location=Lagos, why is vegetation this low?", get genuinely useful responses. The model responds well to being given context rather than being expected to know it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Project Matters
&lt;/h2&gt;

&lt;p&gt;Most satellite analysis tools assume you already understand the data. They display a map and leave interpretation to the user. TerraSight flips that the map is secondary to understanding what the map means.&lt;/p&gt;

&lt;p&gt;That shift matters most for the people who aren't GIS professionals but still have legitimate reasons to analyse land cover: a farmer assessing crop stress after a dry month, a local government tracking heat island growth, a student building a remote sensing project for the first time. None of them needs ENVI. They need something that takes their files, applies the correct math, and tells them what they're looking at.&lt;/p&gt;

&lt;p&gt;Gemma 4 E4B makes that possible without a cloud subscription or a data privacy tradeoff. The model is small enough to run locally, capable enough to interpret geospatial statistics coherently, and fast enough that the chat interface feels responsive rather than like waiting for an API call.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with Python, Streamlit, Rasterio, GeoPandas, and Gemma 4 E4B via Ollama.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Data from USGS EarthExplorer (Landsat 9 Collection 2 Level-2).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gemmachallenge</category>
      <category>gemma</category>
    </item>
  </channel>
</rss>
