<?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>Forem: Jan Tschada</title>
    <description>The latest articles on Forem by Jan Tschada (@gisfromscratch).</description>
    <link>https://forem.com/gisfromscratch</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%2F1082802%2Fc9070a2d-db36-4335-afd9-8d903ddcd191.png</url>
      <title>Forem: Jan Tschada</title>
      <link>https://forem.com/gisfromscratch</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gisfromscratch"/>
    <language>en</language>
    <item>
      <title>How We Built a Geospatial AI That Reads Satellite Images</title>
      <dc:creator>Jan Tschada</dc:creator>
      <pubDate>Thu, 30 Apr 2026 04:08:27 +0000</pubDate>
      <link>https://forem.com/gisfromscratch/how-we-built-a-geospatial-ai-that-reads-satellite-images-296</link>
      <guid>https://forem.com/gisfromscratch/how-we-built-a-geospatial-ai-that-reads-satellite-images-296</guid>
      <description>&lt;p&gt;A developer’s guide to &lt;code&gt;analyze_image&lt;/code&gt;, &lt;code&gt;analyze_text&lt;/code&gt;, and the future of location intelligence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The motivation
&lt;/h2&gt;

&lt;p&gt;You’re a developer. You know that satellite imagery is everywhere. But most GeoAI solutions are either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Black‑box desktop tools with 500 buttons&lt;/li&gt;
&lt;li&gt;Intelligence‑grade surveillance systems you’d never touch&lt;/li&gt;
&lt;li&gt;Overhyped pixel classifiers that can’t tell a port from a parking lot&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  We wanted something different:
&lt;/h2&gt;

&lt;p&gt;A code‑first, ethical, and actually useful geospatial AI that runs in a Jupyter notebook.&lt;/p&gt;

&lt;p&gt;So we built it with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;arcgis.ai.analyze_image&lt;/code&gt; – to read satellite photos&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;arcgis.ai.analyze_text&lt;/code&gt; – to write structured reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few dozen lines of Python, and &lt;a href="https://location.arcgis.com/features/" rel="noopener noreferrer"&gt;ArcGIS Location Platform (beta AI)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No mouse clicks. Just code.&lt;/p&gt;

&lt;p&gt;In this post, I’ll show you exactly how it works – including real snippets from our &lt;code&gt;platform.py&lt;/code&gt; – so you can steal the ideas (or the whole notebook) for your own projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea (Super Simple)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;User pans a map to any location.&lt;/li&gt;
&lt;li&gt;We export that exact view as a georeferenced PNG.&lt;/li&gt;
&lt;li&gt;We send the image + a strict, no‑speculation prompt to our beta AI endpoint using &lt;code&gt;analyze_image&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The AI returns a natural‑language description.&lt;/li&gt;
&lt;li&gt;We feed that description + location data into &lt;code&gt;analyze_text&lt;/code&gt; with a report template.&lt;/li&gt;
&lt;li&gt;Out comes a clean Markdown report (Location, Features, Patterns, Risks, Assessment).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s it. No complex pipelines. No training custom models. Just clever prompting and two AI functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code You Actually Care About
&lt;/h2&gt;

&lt;p&gt;Our entire workflow is in &lt;code&gt;platform.py&lt;/code&gt;. Let me walk you through the juicy parts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Export a map image (no screenshot hacks)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# author: Jan Tschada
# SPDX-License-Identifer: Apache-2.0
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;export_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map_view&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ExportedMapImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;bbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;map_view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extent&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;xmin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;map_view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extent&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ymin&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;map_view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extent&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;xmax&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;map_view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extent&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ymax&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;bbox_sr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;map_view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extent&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;spatialReference&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;wkid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;map_layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MapImageLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ExportedMapImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;map_export_result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;map_layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;export_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bbox&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bbox_sr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bbox_sr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;bbox&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bbox&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bbox_sr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bbox_sr&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why this is cool:
&lt;/h3&gt;

&lt;p&gt;The exported image is georeferenced, so we know exactly which coordinates each pixel covers. That means we can later tell the AI: &lt;em&gt;"You’re looking at 25.02° N, 55.04° E – the Port of Jebel Ali."&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Download image bytes (streaming, memory‑efficient)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# author: Jan Tschada
# SPDX-License-Identifer: Apache-2.0
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;download_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;file_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iter_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;file_bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;file_bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;file_bytes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing fancy, but it’s solid. We stream the image in 8KB chunks so we don’t blow up memory on huge exports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reverse geocode the image center
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# author: Jan Tschada
# SPDX-License-Identifer: Apache-2.0
&lt;/span&gt;
&lt;span class="n"&gt;gps_extent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;project&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;exported_image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map_export_result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;extent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;in_sr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bbox_sr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_sr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;xmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ymin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xmax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ymax&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gps_extent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;gps_lon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xmin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xmax&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;xmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gps_lat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ymin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ymax&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ymin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;geocoding_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;reverse_geocode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gps_lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;y&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gps_lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;spatialReference&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wkid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4326&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="n"&gt;lang_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;geocoding_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address&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;Now we have a human‑readable address (e.g., &lt;em&gt;"Port of Jebel Ali, Dubai, UAE"&lt;/em&gt;) to feed into the AI prompts.&lt;/p&gt;

&lt;h3&gt;
  
  
  The secret sauce: a disciplined prompt for analyze_image
&lt;/h3&gt;

&lt;p&gt;This is where most people mess up. They ask, "Tell me everything about this place." Then the AI hallucinates.&lt;/p&gt;

&lt;p&gt;We do the opposite:&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;# author: Jan Tschada
# SPDX-License-Identifer: Apache-2.0
&lt;/span&gt;
&lt;span class="n"&gt;ai_prompt&lt;/span&gt; &lt;span class="o"&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;
    You are a geospatial imagery analyst.
    Analyze the satellite image using only visible evidence.
    State uncertainty explicitly. Do not speculate.

    Location: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gps_lat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gps_lon&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LongLabel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only what you see&lt;/li&gt;
&lt;li&gt;If unsure, say so&lt;/li&gt;
&lt;li&gt;No guessing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This dramatically reduces hallucinations. The AI will say &lt;em&gt;"I see what looks like a port, but I cannot confirm the exact type"&lt;/em&gt; instead of inventing a naval base.&lt;/p&gt;

&lt;h3&gt;
  
  
  Call analyze_image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# author: Jan Tschada
# SPDX-License-Identifer: Apache-2.0
&lt;/span&gt;
&lt;span class="n"&gt;image_base64&lt;/span&gt; &lt;span class="o"&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;data:image/png;base64,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image_bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;analyze_image_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;analyze_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;image_base64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ai_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;portal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EN&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;That’s it. One line. The gis instance is your authenticated ArcGIS portal connection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate a structured report with analyze_text
&lt;/h3&gt;

&lt;p&gt;We take the image analysis output and wrap it into a report template.&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;# author: Jan Tschada
# SPDX-License-Identifer: Apache-2.0
&lt;/span&gt;
&lt;span class="n"&gt;ai_summarize_prompt&lt;/span&gt; &lt;span class="o"&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;
    You are a senior geospatial analyst.
    Create a professional situational analysis report.
    Output MUST be valid Markdown.
    Sections:
    ## Location
    ## Observed Features
    ## Activity &amp;amp; Patterns
    ## Risk &amp;amp; Threat Assessment
    ## Analyst Assessment
    Write concisely, analytically.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="n"&gt;intel_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image_analysis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;analyze_image_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latitude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gps_lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;longitude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;gps_lon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;analyze_text_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;analyze_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intel_data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ai_summarize_prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;portal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;EN&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;Boom. The second AI reorganises the raw description into a clean, sectioned report. No manual parsing required.&lt;/p&gt;

&lt;p&gt;Here’s what the AI actually produced for one of our test ports:&lt;/p&gt;

&lt;h2&gt;
  
  
  Situational Analysis Report - Date 2026-04-20T10:15:32
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbrsbpblc5wvvdtrndyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgbrsbpblc5wvvdtrndyh.png" alt="Imagery" width="720" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Location
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Geocoordinates:&lt;/strong&gt; &lt;em&gt;25.0137 N, 55.0743 E&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Identified Site:&lt;/strong&gt; &lt;em&gt;Mena Jabal Ali, Dubai, United Arab Emirates&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Area Overview:&lt;/strong&gt; &lt;em&gt;Situated in the Jebel Ali district of Dubai, encompassing significant coastal, urban, and artificial landforms.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Observed Features
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Prominent artificial island resembling a palm tree (Palm Jebel Ali).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Urban coastal infrastructure comprising dense road networks, residential zones, and potential industrial facilities.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Multiple marinas and docks located along the shoreline, enhancing maritime accessibility.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Visible boundaries distinguishing developed urban tracts from adjacent desert areas.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pockets of sparse vegetation and undeveloped open land interspersed throughout the region.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Activity &amp;amp; Patterns
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Extensive urban development and engineered expansions, typified by the artificial island and substantial coastal modification, suggest large-scale planning and ongoing construction activity.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The presence of marinas and docks points to active maritime transit and probable commercial shipping or recreational boating.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The orderly distribution of residential and industrial areas implies strategic urban zoning.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Noticeable demarcations between green/open land and developed sectors reflect active land management and expansion.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Risk &amp;amp; Threat Assessment
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;The concentration of critical urban and maritime infrastructure in a narrow coastal corridor increases vulnerability to natural hazards (e.g., sea-level rise, storms) and potential deliberate disruptions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;High-profile artificial landforms may attract heightened attention and pose elevated operational and maintenance risks.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The juxtaposition of industrial, residential, and transport nodes requires robust monitoring to mitigate economic or environmental threats.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Analyst Assessment
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Palm Jebel Ali and the surrounding Jebel Ali area represent a nexus of strategic urban expansion, maritime operations, and infrastructural development within Dubai. The imagery confirms sustained investment in coastal engineering and the diversification of land use.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;While the overall stability of the site appears high, continued monitoring is recommended to track further urban growth, shifting coastal dynamics, and associated risk profiles, given the region’s prominence and developmental trajectory.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Further detail regarding specific facility functions, population density, or economic assets is not ascertainable from imagery alone.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Coming Next (Roadmap – and we need your help)
&lt;/h2&gt;

&lt;p&gt;Our current version only looks at the image. But an image doesn’t tell you everything.&lt;/p&gt;

&lt;p&gt;We plan to integrate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenStreetMap – roads, building footprints, land use, ports&lt;/li&gt;
&lt;li&gt;Wikidata – facts about the place (population, founding year, official name)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then we’ll feed that structured data into analyze_text alongside the image analysis. Imagine the AI saying:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"I see a large building. OSM says it’s a hospital. Wikidata tells me it was built in 1985 and has 200 beds."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s real context‑aware geospatial AI.&lt;/p&gt;

&lt;p&gt;We’d love your input:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which OSM tags are most valuable for your use cases?&lt;/li&gt;
&lt;li&gt;How would you improve the prompt to reduce hallucinations further?&lt;/li&gt;
&lt;li&gt;Would you use this in production? Why / why not?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’re not building a black‑box image intelligence tool. We’re building an open, ethical, developer‑friendly geospatial AI.&lt;/p&gt;

&lt;p&gt;If that resonates with you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comment below with your craziest geospatial AI idea&lt;/li&gt;
&lt;li&gt;Share this post with one colleague who might find it useful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s make geospatial intelligence accessible to every developer.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>geospatial</category>
      <category>imagery</category>
    </item>
    <item>
      <title>Spatial Data Science: A New Way to Solve Problems</title>
      <dc:creator>Jan Tschada</dc:creator>
      <pubDate>Fri, 17 Oct 2025 06:26:41 +0000</pubDate>
      <link>https://forem.com/gisfromscratch/spatial-data-science-a-new-way-to-solve-problems-1cim</link>
      <guid>https://forem.com/gisfromscratch/spatial-data-science-a-new-way-to-solve-problems-1cim</guid>
      <description>&lt;p&gt;Welcome to the world of Spatial Data Science in ArcGIS! In this section, we’re going to explore a simple but powerful idea: thinking about where things happen can help us solve some of our most complex challenges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It All Starts with a Question&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the heart of this approach is something we call "The Geographic Approach." This is just a fancy way of saying we start with the problem itself, not with a specific tool or piece of software. It’s a mindset that encourages us to ask the right questions about location, such as:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📍 Where is it happening? (For example, where are the traffic accidents concentrated?)

⁉️ Why is it happening there? (What is it about those specific intersections that makes them dangerous?)

🗺️ What’s nearby? (Are the accidents close to schools, bars, or major highways?)

❓ How is it changing? (Is the problem getting better or worse over time?)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By starting with these simple questions, we can begin to see the world in a new way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spatial Analysis: The Language of Location
&lt;/h2&gt;

&lt;p&gt;When we use maps and data to answer these "where" questions, we are doing spatial analysis. Think of it as learning a new language—the language of location. This language helps us uncover hidden patterns, relationships, and trends that we might completely miss if we just looked at spreadsheets or lists.&lt;/p&gt;

&lt;p&gt;Whether we're planning a new park, monitoring the health of a forest, or figuring out the best location for a new store, geography provides the essential context. It connects the dots and tells the full story.&lt;/p&gt;

&lt;p&gt;Keep an eye out for our next section, where we’ll share real-world examples, live demos, and step-by-step guides that show how we bring data to life using &lt;a href="https://developers.arcgis.com/python/latest/guide/part1-introduction-to-dataengineering/" rel="noopener noreferrer"&gt;ArcGIS&lt;/a&gt;. You’ll see just how powerful it can be to ask, "Where?"&lt;/p&gt;

&lt;p&gt;📍&lt;a href="https://www.arcgis.com/apps/mapviewer/index.html?center=8.643919,50.111592&amp;amp;level=16" rel="noopener noreferrer"&gt;Location&lt;/a&gt;&lt;br&gt;
🎬 &lt;a href="https://registration.esri.com/flow/esri/25euroepcdev/deveventportal/page/detailed-agenda/session/1756970478006001sn4z" rel="noopener noreferrer"&gt;Technical Session&lt;/a&gt;&lt;/p&gt;

</description>
      <category>geography</category>
      <category>datascience</category>
      <category>python</category>
      <category>knowledge</category>
    </item>
    <item>
      <title>Mapping the geospatial patterns of broadcasted news</title>
      <dc:creator>Jan Tschada</dc:creator>
      <pubDate>Fri, 19 May 2023 16:53:15 +0000</pubDate>
      <link>https://forem.com/gisfromscratch/mapping-the-geospatial-patterns-of-broadcasted-news-56nh</link>
      <guid>https://forem.com/gisfromscratch/mapping-the-geospatial-patterns-of-broadcasted-news-56nh</guid>
      <description>&lt;p&gt;Mapping the geospatial patterns of broadcasted news allows a geospatial analyst to gain insights into common and unusual geospatial patterns. We decided using one of the most comprehensive news collection named "Global Database Events of Tone and Language" (GDELT) as the ground truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The geography of worldwide news coverage
&lt;/h2&gt;

&lt;p&gt;Understanding the geospatial patterns of such a massive knowledge graph is often difficult for non geospatial experts. The machine learning algorithms extract articles and features from websites in real-time. The geocoding engine matches extracted locations against over eleven million well-known place names. An article mentioning a place like "New York" leads to an extracted feature location. But, the article does not have to be specific about the named location. For instance, an article regarding "The impact of the COVID pandemic on capital markets." — mentioning "New York is no exception" — leads to a location match. We should expect some false positives, but the sum of all extracted locations should reflect a geospatial pattern and give us a coarse-grained overview.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing the geospatial features using the geoprotests API
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://rapidapi.com/gisfromscratch/api/geoprotests/details" rel="noopener noreferrer"&gt;geoprotests API&lt;/a&gt; offer ready-to-use geospatial features representing broadcasted news related to protests and demonstrations. You can use these geospatial features to build various mapping and geospatial applications.&lt;/p&gt;

&lt;p&gt;Every geospatial result support the GeoJSON and Esri FeatureSet format out of the box. All endpoints support an optional date parameter for filtering the results. For best performance, the serverless cloud-backend calculate the geospatial aggregations of the last 24 hours between midnight and 1 AM. The serverless functions save these geospatial features and yesterday should be the latest available date. Without specifying a date, we calculate the geospatial features of the last 24 hours on-the-fly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ramp up your development environment
&lt;/h3&gt;

&lt;p&gt;You need to activate your Rapid API account. Please, check out the &lt;a href="https://docs.rapidapi.com/docs/account-creation-and-settings" rel="noopener noreferrer"&gt;RapidAPI Account Creation and Management Guide&lt;/a&gt; for more details.&lt;/p&gt;

&lt;p&gt;Setup your Python based development environment using your weapon of choice. Using pip creating a virtual environment is one simple approach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv geoint

&lt;span class="c"&gt;# Linux&lt;/span&gt;
&lt;span class="nb"&gt;source &lt;/span&gt;geoint/bin/activate

&lt;span class="c"&gt;# Windows&lt;/span&gt;
geoint/Scripts/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Python module requests offers easy and elegant access to http functions. You need to install this module using pip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Accessing the broadcasted news of 31th December 2022
&lt;/h3&gt;

&lt;p&gt;The hotspot endpoint offers access to statistically significant named locations. The count of news mentioning these locations define the degree of significance.&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;# author: Jan Tschada
# SPDX-License-Identifer: Apache-2.0
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://geoprotests.p.rapidapi.com/hotspots&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="n"&gt;querystring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2022-12-31&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;geojson&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Authenticate: https://rapidapi.com/auth
&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;SIGN-UP-FOR-KEY&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;geoprotests.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;geojson_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;querystring&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;geojson_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;geojson_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The features dictionary represents the named locations in GeoJSON format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"FeatureCollection"&lt;/span&gt;,
  &lt;span class="s2"&gt;"features"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"Feature"&lt;/span&gt;,
      &lt;span class="s2"&gt;"id"&lt;/span&gt;: 41415,
      &lt;span class="s2"&gt;"geometry"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"Point"&lt;/span&gt;,
        &lt;span class="s2"&gt;"coordinates"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
          37.6156,
          55.7522
        &lt;span class="o"&gt;]&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;,
      &lt;span class="s2"&gt;"properties"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"OBJECTID"&lt;/span&gt;: 41415,
        &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"Moscow, Moskva, Russia"&lt;/span&gt;,
        &lt;span class="s2"&gt;"timestamp"&lt;/span&gt;: &lt;span class="s2"&gt;"2022-12-31T00:00:00"&lt;/span&gt;,
        &lt;span class="s2"&gt;"count"&lt;/span&gt;: 77
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Accessing the aggregated broadcasted news
&lt;/h3&gt;

&lt;p&gt;The spatial aggregations are hexagonal geospatial features having a specific property representing the count of the news mentioning a location being within a hexagonal grid cell. Spatial analyst's use mapping capabilities visualizing hot and cold spots of these spatial aggregations.&lt;/p&gt;

&lt;p&gt;Let us use the powerful mapping capabilities of ArcGIS and visualize the aggregated broadcasted news as a feature set. You need to specify the output format to esri.&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;# author: Jan Tschada
# SPDX-License-Identifer: Apache-2.0
&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://geoprotests.p.rapidapi.com/aggregate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="n"&gt;querystring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2022-12-31&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;esri&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;geoprotests.p.rapidapi.com&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;esri_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;querystring&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;esri_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;aggregated_features&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;esri_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The aggregated features represent the corresponding hexagonal grid cells as a Python dictionary. You need to setup the arcgis Python Module into your development environment. The contained map widget offers powerful mapping capabilities for your Jupyter Notebook environment. Follow the &lt;a href="https://developers.arcgis.com/python/guide/install-and-set-up/" rel="noopener noreferrer"&gt;Install and Setup Guide&lt;/a&gt;.&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;# author: Jan Tschada
# SPDX-License-Identifer: Apache-2.0
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arcgis.gis&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;GIS&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arcgis.features&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FeatureSet&lt;/span&gt;

&lt;span class="c1"&gt;# Create a FeatureSet from the features
&lt;/span&gt;&lt;span class="n"&gt;aggregated_featureset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;FeatureSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aggregated_features&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Anonousmly connect to ArcGIS Online
&lt;/span&gt;&lt;span class="n"&gt;gis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GIS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Create a simple map view
&lt;/span&gt;&lt;span class="n"&gt;map_view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Europe&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Add the FeatureSet as a layer
&lt;/span&gt;&lt;span class="n"&gt;map_view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_layer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aggregated_featureset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi5i58umg6ilt2w8ppg5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi5i58umg6ilt2w8ppg5.png" alt="Map view in a Jupyter notebook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should tweak the rendering of the features using a dedicated renderer instance. The renderer is an optional parameter of the add_layer method. But, you could also use a different approach for mapping the aggregated features. The spatial-enabled dataframe offers plotting capabilities in combination with the map view. So that you can easily define rendering supporting natural class breaks.&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;# Create a simple map view
&lt;/span&gt;&lt;span class="n"&gt;map_view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Europe&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Add the FeatureSet as a layer
&lt;/span&gt;&lt;span class="n"&gt;aggregated_featureset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spatial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;map_view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                      &lt;span class="n"&gt;renderer_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;c&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                      &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;esriClassifyNaturalBreaks&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                      &lt;span class="n"&gt;class_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                      &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;count&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                      &lt;span class="n"&gt;cmap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;YlOrRd&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                      &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.35&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The produced map shows the hotspot in red being Moscow, Russia with a mention count of 77, as we expected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplvk5dkeg4uauhj2w4of.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplvk5dkeg4uauhj2w4of.png" alt="Map of aggregated news related to protests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any feedback is welcome, keep on mapping!&lt;/p&gt;

&lt;p&gt;Feel free to try it out: &lt;a href="https://rapidapi.com/gisfromscratch/api/geoprotests/details" rel="noopener noreferrer"&gt;geoprotests API&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>geoint</category>
      <category>python</category>
      <category>spatial</category>
      <category>arcgis</category>
    </item>
  </channel>
</rss>
