<?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: TaroFortune</title>
    <description>The latest articles on DEV Community by TaroFortune (@tarofortune).</description>
    <link>https://dev.to/tarofortune</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3934770%2F30160537-5d02-45ce-a74e-0e51282c334f.png</url>
      <title>DEV Community: TaroFortune</title>
      <link>https://dev.to/tarofortune</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tarofortune"/>
    <language>en</language>
    <item>
      <title>PWA with React + Leaflet: lessons from a 31-city map app</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Sun, 30 Aug 2026 20:01:08 +0000</pubDate>
      <link>https://dev.to/tarofortune/pwa-with-react-leaflet-lessons-from-a-31-city-map-app-5agc</link>
      <guid>https://dev.to/tarofortune/pwa-with-react-leaflet-lessons-from-a-31-city-map-app-5agc</guid>
      <description>&lt;p&gt;Notes from shipping a real-world PWA in Korea:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vite SSG is worth it. SPAs lose Korean SEO because Naver's crawler doesn't fully render JS.&lt;/li&gt;
&lt;li&gt;Leaflet &amp;gt; Mapbox for cost-sensitive projects. Free OSM tiles + zero cost.&lt;/li&gt;
&lt;li&gt;Firebase Hosting rewrites let you have deep URLs (/city/suwon) without a backend.&lt;/li&gt;
&lt;li&gt;KakaoTalk share is non-negotiable in Korea — 70%+ of social traffic.&lt;/li&gt;
&lt;li&gt;PWA install banner: don't auto-prompt, let the user discover.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Project: &lt;a href="https://gyeonggi-currency-map.web.app?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://gyeonggi-currency-map.web.app?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>react</category>
      <category>webdev</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Simulating winter sunlight for 38,000 apartment complexes (terrain + building shadows)</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Sat, 29 Aug 2026 19:46:58 +0000</pubDate>
      <link>https://dev.to/tarofortune/simulating-winter-sunlight-for-38000-apartment-complexes-terrain-building-shadows-28k5</link>
      <guid>https://dev.to/tarofortune/simulating-winter-sunlight-for-38000-apartment-complexes-terrain-building-shadows-28k5</guid>
      <description>&lt;p&gt;In Korea, "how much winter sun does this apartment get?" is a real buying question, not a nice-to-have. And the honest answer isn't "look at the balcony direction" — it's a geometry problem with three moving parts: where the sun actually is in the sky in December, what hills and mountains sit on the horizon, and whether the building directly in front casts a shadow over you.&lt;/p&gt;

&lt;p&gt;I built ZipScope (집스코프), a Korean living-area analysis map that scores 38,000+ apartment, officetel, and low-rise complexes nationwide across a seven-dimension living score — transit, schools, amenities, healthcare, environment, culture, and future value. Alongside that score, it also runs a set of terrain-based estimates: winter daylight, flood risk, and waterfront views. This post is about how the winter-sunlight simulation works. You can poke at the live version here: &lt;a href="https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the winter solstice is the only day that matters
&lt;/h2&gt;

&lt;p&gt;If you want a single worst-case number for daylight, you compute it on the winter solstice (동지). That's when the sun's arc across the sky is at its lowest and shortest for the whole year. A unit that gets decent light on the solstice gets at least that much every other day. A unit that's already blocked on the solstice is the one you warn people about.&lt;/p&gt;

&lt;p&gt;So the whole simulation is anchored to one day: trace the sun's path from sunrise to sunset on the solstice, and at each moment ask a single yes/no question — &lt;em&gt;can this complex actually see the sun right now, or is something in the way?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: the sun's winter arc
&lt;/h2&gt;

&lt;p&gt;The sun's position for any instant is a textbook astronomy calculation. Two angles describe it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;elevation&lt;/strong&gt; — how high above the horizon the sun is (0° at the horizon, 90° straight up)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;azimuth&lt;/strong&gt; — which compass direction it's in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the solstice the sun's declination bottoms out near −23.4°, so in Korean latitudes the noon sun never climbs very high — it skims low across the southern sky. That low arc is exactly why front buildings and terrain matter so much in winter: the sun is coming in at a shallow angle, so even a modest obstruction reaches up and clips it.&lt;/p&gt;

&lt;p&gt;Stepping through the day in small time increments gives a sequence of (azimuth, elevation) pairs — the sun's track. That's the thing everything else gets tested against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: the terrain horizon from a DEM
&lt;/h2&gt;

&lt;p&gt;The sky isn't a flat 0° horizon. If there's a ridge to the south-east, the sun doesn't "rise" for that location until it clears the ridge. To capture this I use SRTM 30 m satellite DEM (digital elevation model) data.&lt;/p&gt;

&lt;p&gt;For a given complex I sample the DEM outward along many compass directions. In each direction I walk away from the point and compute the angle up to each terrain sample:&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;# horizon angle in one azimuth direction
&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;distances_along_ray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;dz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;elevation_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;elevation_here&lt;/span&gt;
    &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# elevation angle to that terrain point
&lt;/span&gt;    &lt;span class="n"&gt;horizon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# keep the highest blocker
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do that for every direction and you get a &lt;strong&gt;horizon profile&lt;/strong&gt;: the minimum sun elevation needed to be visible in each azimuth. A location down in a valley has a high horizon profile; one on a hillside facing south has a low one. Sitting on the DEM also means every complex gets an honest elevation-above-sea-level readout as a by-product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: the front-building shadow angle
&lt;/h2&gt;

&lt;p&gt;Terrain explains the macro picture; the building directly in front (앞동) explains why apartment 3F is dark at noon while 15F is bright. A facing block subtends a shadow angle that depends on how tall it is and how far away it stands — and because winter sun comes in low, a fairly ordinary front building can block a surprising amount of it.&lt;/p&gt;

&lt;p&gt;The test is the same shape as the terrain one: the front structure contributes its own elevation angle in the azimuth range it occupies. Combine it with the terrain horizon by simply taking the higher blocker in each direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;The visibility test for every sampled moment of the solstice day becomes one comparison:&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;sunny_minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elev&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sun_track&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;            &lt;span class="c1"&gt;# sun path across the solstice
&lt;/span&gt;    &lt;span class="n"&gt;blocker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;terrain_horizon&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# hills / mountains
&lt;/span&gt;                  &lt;span class="n"&gt;building_shadow&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# the block in front
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;elev&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;blocker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sunny_minutes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step_minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sum the minutes where the sun clears every obstruction and you have a defensible winter-daylight estimate — not a vibe, a traced sun path checked against real terrain and real geometry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it 38,000 times
&lt;/h2&gt;

&lt;p&gt;Running this per complex, on demand, would be slow, so the heavy geometry is precomputed and the results stored. The stack is deliberately boring and cheap: Flask + React + Leaflet on the front, SQLite for storage, SRTM DEM for terrain and OSM Overpass for map features, all hosted on PythonAnywhere. Boring is a feature — it keeps the whole thing free to run for a nationwide dataset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honesty as a design constraint
&lt;/h2&gt;

&lt;p&gt;One rule I hold across the whole product: every estimate ships with its source and its limits. The sunlight figure is a &lt;em&gt;simulation&lt;/em&gt;, not a survey — a solstice-anchored model built from a 30 m DEM and building geometry, and it's labeled that way in the UI. Same discipline applies elsewhere in the app (the flood-risk layer, for instance, is clearly marked as an estimate and points users to the official flood map rather than pretending to replace it).&lt;/p&gt;

&lt;p&gt;It turns out "here's the number, and here's exactly how we got it and where it can be wrong" is not just good engineering hygiene — for a service asking people to trust a map with a housing decision, it's the whole pitch.&lt;/p&gt;

&lt;p&gt;— austriano&lt;/p&gt;

</description>
      <category>gis</category>
      <category>python</category>
      <category>algorithms</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Simulating winter sunlight for 38,000 apartment complexes (terrain + building shadows)</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Sat, 29 Aug 2026 01:22:19 +0000</pubDate>
      <link>https://dev.to/tarofortune/simulating-winter-sunlight-for-38000-apartment-complexes-terrain-building-shadows-a7</link>
      <guid>https://dev.to/tarofortune/simulating-winter-sunlight-for-38000-apartment-complexes-terrain-building-shadows-a7</guid>
      <description>&lt;p&gt;In Korea, "how much winter sun does this apartment get?" is a real buying question, not a nice-to-have. And the honest answer isn't "look at the balcony direction" — it's a geometry problem with three moving parts: where the sun actually is in the sky in December, what hills and mountains sit on the horizon, and whether the building directly in front casts a shadow over you.&lt;/p&gt;

&lt;p&gt;I built ZipScope (집스코프), a Korean living-area analysis map that scores 38,000+ apartment, officetel, and low-rise complexes nationwide across a seven-dimension living score — transit, schools, amenities, healthcare, environment, culture, and future value. Alongside that score, it also runs a set of terrain-based estimates: winter daylight, flood risk, and waterfront views. This post is about how the winter-sunlight simulation works. You can poke at the live version here: &lt;a href="https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the winter solstice is the only day that matters
&lt;/h2&gt;

&lt;p&gt;If you want a single worst-case number for daylight, you compute it on the winter solstice (동지). That's when the sun's arc across the sky is at its lowest and shortest for the whole year. A unit that gets decent light on the solstice gets at least that much every other day. A unit that's already blocked on the solstice is the one you warn people about.&lt;/p&gt;

&lt;p&gt;So the whole simulation is anchored to one day: trace the sun's path from sunrise to sunset on the solstice, and at each moment ask a single yes/no question — &lt;em&gt;can this complex actually see the sun right now, or is something in the way?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: the sun's winter arc
&lt;/h2&gt;

&lt;p&gt;The sun's position for any instant is a textbook astronomy calculation. Two angles describe it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;elevation&lt;/strong&gt; — how high above the horizon the sun is (0° at the horizon, 90° straight up)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;azimuth&lt;/strong&gt; — which compass direction it's in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the solstice the sun's declination bottoms out near −23.4°, so in Korean latitudes the noon sun never climbs very high — it skims low across the southern sky. That low arc is exactly why front buildings and terrain matter so much in winter: the sun is coming in at a shallow angle, so even a modest obstruction reaches up and clips it.&lt;/p&gt;

&lt;p&gt;Stepping through the day in small time increments gives a sequence of (azimuth, elevation) pairs — the sun's track. That's the thing everything else gets tested against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: the terrain horizon from a DEM
&lt;/h2&gt;

&lt;p&gt;The sky isn't a flat 0° horizon. If there's a ridge to the south-east, the sun doesn't "rise" for that location until it clears the ridge. To capture this I use SRTM 30 m satellite DEM (digital elevation model) data.&lt;/p&gt;

&lt;p&gt;For a given complex I sample the DEM outward along many compass directions. In each direction I walk away from the point and compute the angle up to each terrain sample:&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;# horizon angle in one azimuth direction
&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;distances_along_ray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;dz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;elevation_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;elevation_here&lt;/span&gt;
    &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# elevation angle to that terrain point
&lt;/span&gt;    &lt;span class="n"&gt;horizon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# keep the highest blocker
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do that for every direction and you get a &lt;strong&gt;horizon profile&lt;/strong&gt;: the minimum sun elevation needed to be visible in each azimuth. A location down in a valley has a high horizon profile; one on a hillside facing south has a low one. Sitting on the DEM also means every complex gets an honest elevation-above-sea-level readout as a by-product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: the front-building shadow angle
&lt;/h2&gt;

&lt;p&gt;Terrain explains the macro picture; the building directly in front (앞동) explains why apartment 3F is dark at noon while 15F is bright. A facing block subtends a shadow angle that depends on how tall it is and how far away it stands — and because winter sun comes in low, a fairly ordinary front building can block a surprising amount of it.&lt;/p&gt;

&lt;p&gt;The test is the same shape as the terrain one: the front structure contributes its own elevation angle in the azimuth range it occupies. Combine it with the terrain horizon by simply taking the higher blocker in each direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;The visibility test for every sampled moment of the solstice day becomes one comparison:&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;sunny_minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elev&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sun_track&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;            &lt;span class="c1"&gt;# sun path across the solstice
&lt;/span&gt;    &lt;span class="n"&gt;blocker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;terrain_horizon&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# hills / mountains
&lt;/span&gt;                  &lt;span class="n"&gt;building_shadow&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# the block in front
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;elev&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;blocker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sunny_minutes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step_minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sum the minutes where the sun clears every obstruction and you have a defensible winter-daylight estimate — not a vibe, a traced sun path checked against real terrain and real geometry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it 38,000 times
&lt;/h2&gt;

&lt;p&gt;Running this per complex, on demand, would be slow, so the heavy geometry is precomputed and the results stored. The stack is deliberately boring and cheap: Flask + React + Leaflet on the front, SQLite for storage, SRTM DEM for terrain and OSM Overpass for map features, all hosted on PythonAnywhere. Boring is a feature — it keeps the whole thing free to run for a nationwide dataset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honesty as a design constraint
&lt;/h2&gt;

&lt;p&gt;One rule I hold across the whole product: every estimate ships with its source and its limits. The sunlight figure is a &lt;em&gt;simulation&lt;/em&gt;, not a survey — a solstice-anchored model built from a 30 m DEM and building geometry, and it's labeled that way in the UI. Same discipline applies elsewhere in the app (the flood-risk layer, for instance, is clearly marked as an estimate and points users to the official flood map rather than pretending to replace it).&lt;/p&gt;

&lt;p&gt;It turns out "here's the number, and here's exactly how we got it and where it can be wrong" is not just good engineering hygiene — for a service asking people to trust a map with a housing decision, it's the whole pitch.&lt;/p&gt;

&lt;p&gt;— austriano&lt;/p&gt;

</description>
      <category>gis</category>
      <category>python</category>
      <category>algorithms</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why I built a real-estate tool with zero real-estate listings</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Wed, 26 Aug 2026 17:01:39 +0000</pubDate>
      <link>https://dev.to/tarofortune/why-i-built-a-real-estate-tool-with-zero-real-estate-listings-2lij</link>
      <guid>https://dev.to/tarofortune/why-i-built-a-real-estate-tool-with-zero-real-estate-listings-2lij</guid>
      <description>&lt;p&gt;Every property site in Korea (Hogang-no-no, Zigbang, Dabang) shows the same listings with the same agents.&lt;/p&gt;

&lt;p&gt;What people actually need when moving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How far is the subway? (door to platform)&lt;/li&gt;
&lt;li&gt;Can my kid walk to school?&lt;/li&gt;
&lt;li&gt;Is there a mart within 10 min on foot?&lt;/li&gt;
&lt;li&gt;How long to my workplace, with transfers?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's what I built: &lt;a href="https://geoinfomatic.pythonanywhere.com?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://geoinfomatic.pythonanywhere.com?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No listings. No agents. Just an honest accessibility map.&lt;/p&gt;

&lt;p&gt;Funded by 5 free analyses/day + 9,900 KRW Pro tier. Built solo with Flask + Leaflet.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>civictech</category>
      <category>leaflet</category>
      <category>showdev</category>
    </item>
    <item>
      <title>PWA with React + Leaflet: lessons from a 31-city map app</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Tue, 25 Aug 2026 18:31:19 +0000</pubDate>
      <link>https://dev.to/tarofortune/pwa-with-react-leaflet-lessons-from-a-31-city-map-app-17go</link>
      <guid>https://dev.to/tarofortune/pwa-with-react-leaflet-lessons-from-a-31-city-map-app-17go</guid>
      <description>&lt;p&gt;Notes from shipping a real-world PWA in Korea:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vite SSG is worth it. SPAs lose Korean SEO because Naver's crawler doesn't fully render JS.&lt;/li&gt;
&lt;li&gt;Leaflet &amp;gt; Mapbox for cost-sensitive projects. Free OSM tiles + zero cost.&lt;/li&gt;
&lt;li&gt;Firebase Hosting rewrites let you have deep URLs (/city/suwon) without a backend.&lt;/li&gt;
&lt;li&gt;KakaoTalk share is non-negotiable in Korea — 70%+ of social traffic.&lt;/li&gt;
&lt;li&gt;PWA install banner: don't auto-prompt, let the user discover.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Project: &lt;a href="https://gyeonggi-currency-map.web.app?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://gyeonggi-currency-map.web.app?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>react</category>
      <category>webdev</category>
      <category>firebase</category>
    </item>
    <item>
      <title>5 Free Tools I Built for Korea: Saju, Maps &amp; Interview Prep</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Mon, 24 Aug 2026 22:29:55 +0000</pubDate>
      <link>https://dev.to/tarofortune/5-free-tools-i-built-for-korea-saju-maps-interview-prep-5f71</link>
      <guid>https://dev.to/tarofortune/5-free-tools-i-built-for-korea-saju-maps-interview-prep-5f71</guid>
      <description>&lt;p&gt;Sharing a few free tools I've built — all free, no signup required.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 🔮 Korean Saju &amp;amp; Tarot
&lt;/h2&gt;

&lt;p&gt;free four-pillar astrology, 78-card tarot, daily fortune &amp;amp; zodiac&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tarofortune.pythonanywhere.com/en?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://tarofortune.pythonanywhere.com/en?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 🗺️ ZipScope
&lt;/h2&gt;

&lt;p&gt;nationwide Korean living-area map: livability score, commute isochrones, flood risk&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 📍 Accessibility Analyzer
&lt;/h2&gt;

&lt;p&gt;isochrone neighborhood analysis — subway/school/hospital within 10-45 min&lt;/p&gt;

&lt;p&gt;&lt;a href="https://geoinfomatic.pythonanywhere.com?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://geoinfomatic.pythonanywhere.com?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 🎤 Interview Copilot
&lt;/h2&gt;

&lt;p&gt;English-interview practice for Korean speakers with an AI interviewer&lt;/p&gt;

&lt;p&gt;&lt;a href="https://geoinfomatic.pythonanywhere.com/interview?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://geoinfomatic.pythonanywhere.com/interview?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 💳 Gyeonggi Currency Map
&lt;/h2&gt;

&lt;p&gt;interactive map of local-currency merchants across Gyeonggi&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gyeonggi-currency-map.web.app?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://gyeonggi-currency-map.web.app?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback welcome!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>korea</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Simulating winter sunlight for 38,000 apartment complexes (terrain + building shadows)</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Mon, 24 Aug 2026 17:44:41 +0000</pubDate>
      <link>https://dev.to/tarofortune/simulating-winter-sunlight-for-38000-apartment-complexes-terrain-building-shadows-3oe2</link>
      <guid>https://dev.to/tarofortune/simulating-winter-sunlight-for-38000-apartment-complexes-terrain-building-shadows-3oe2</guid>
      <description>&lt;p&gt;In Korea, "how much winter sun does this apartment get?" is a real buying question, not a nice-to-have. And the honest answer isn't "look at the balcony direction" — it's a geometry problem with three moving parts: where the sun actually is in the sky in December, what hills and mountains sit on the horizon, and whether the building directly in front casts a shadow over you.&lt;/p&gt;

&lt;p&gt;I built ZipScope (집스코프), a Korean living-area analysis map that scores 38,000+ apartment, officetel, and low-rise complexes nationwide across a seven-dimension living score — transit, schools, amenities, healthcare, environment, culture, and future value. Alongside that score, it also runs a set of terrain-based estimates: winter daylight, flood risk, and waterfront views. This post is about how the winter-sunlight simulation works. You can poke at the live version here: &lt;a href="https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the winter solstice is the only day that matters
&lt;/h2&gt;

&lt;p&gt;If you want a single worst-case number for daylight, you compute it on the winter solstice (동지). That's when the sun's arc across the sky is at its lowest and shortest for the whole year. A unit that gets decent light on the solstice gets at least that much every other day. A unit that's already blocked on the solstice is the one you warn people about.&lt;/p&gt;

&lt;p&gt;So the whole simulation is anchored to one day: trace the sun's path from sunrise to sunset on the solstice, and at each moment ask a single yes/no question — &lt;em&gt;can this complex actually see the sun right now, or is something in the way?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: the sun's winter arc
&lt;/h2&gt;

&lt;p&gt;The sun's position for any instant is a textbook astronomy calculation. Two angles describe it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;elevation&lt;/strong&gt; — how high above the horizon the sun is (0° at the horizon, 90° straight up)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;azimuth&lt;/strong&gt; — which compass direction it's in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the solstice the sun's declination bottoms out near −23.4°, so in Korean latitudes the noon sun never climbs very high — it skims low across the southern sky. That low arc is exactly why front buildings and terrain matter so much in winter: the sun is coming in at a shallow angle, so even a modest obstruction reaches up and clips it.&lt;/p&gt;

&lt;p&gt;Stepping through the day in small time increments gives a sequence of (azimuth, elevation) pairs — the sun's track. That's the thing everything else gets tested against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: the terrain horizon from a DEM
&lt;/h2&gt;

&lt;p&gt;The sky isn't a flat 0° horizon. If there's a ridge to the south-east, the sun doesn't "rise" for that location until it clears the ridge. To capture this I use SRTM 30 m satellite DEM (digital elevation model) data.&lt;/p&gt;

&lt;p&gt;For a given complex I sample the DEM outward along many compass directions. In each direction I walk away from the point and compute the angle up to each terrain sample:&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;# horizon angle in one azimuth direction
&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;distances_along_ray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;dz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;elevation_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;elevation_here&lt;/span&gt;
    &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# elevation angle to that terrain point
&lt;/span&gt;    &lt;span class="n"&gt;horizon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# keep the highest blocker
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do that for every direction and you get a &lt;strong&gt;horizon profile&lt;/strong&gt;: the minimum sun elevation needed to be visible in each azimuth. A location down in a valley has a high horizon profile; one on a hillside facing south has a low one. Sitting on the DEM also means every complex gets an honest elevation-above-sea-level readout as a by-product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: the front-building shadow angle
&lt;/h2&gt;

&lt;p&gt;Terrain explains the macro picture; the building directly in front (앞동) explains why apartment 3F is dark at noon while 15F is bright. A facing block subtends a shadow angle that depends on how tall it is and how far away it stands — and because winter sun comes in low, a fairly ordinary front building can block a surprising amount of it.&lt;/p&gt;

&lt;p&gt;The test is the same shape as the terrain one: the front structure contributes its own elevation angle in the azimuth range it occupies. Combine it with the terrain horizon by simply taking the higher blocker in each direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;The visibility test for every sampled moment of the solstice day becomes one comparison:&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;sunny_minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elev&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sun_track&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;            &lt;span class="c1"&gt;# sun path across the solstice
&lt;/span&gt;    &lt;span class="n"&gt;blocker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;terrain_horizon&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# hills / mountains
&lt;/span&gt;                  &lt;span class="n"&gt;building_shadow&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# the block in front
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;elev&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;blocker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sunny_minutes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step_minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sum the minutes where the sun clears every obstruction and you have a defensible winter-daylight estimate — not a vibe, a traced sun path checked against real terrain and real geometry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it 38,000 times
&lt;/h2&gt;

&lt;p&gt;Running this per complex, on demand, would be slow, so the heavy geometry is precomputed and the results stored. The stack is deliberately boring and cheap: Flask + React + Leaflet on the front, SQLite for storage, SRTM DEM for terrain and OSM Overpass for map features, all hosted on PythonAnywhere. Boring is a feature — it keeps the whole thing free to run for a nationwide dataset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honesty as a design constraint
&lt;/h2&gt;

&lt;p&gt;One rule I hold across the whole product: every estimate ships with its source and its limits. The sunlight figure is a &lt;em&gt;simulation&lt;/em&gt;, not a survey — a solstice-anchored model built from a 30 m DEM and building geometry, and it's labeled that way in the UI. Same discipline applies elsewhere in the app (the flood-risk layer, for instance, is clearly marked as an estimate and points users to the official flood map rather than pretending to replace it).&lt;/p&gt;

&lt;p&gt;It turns out "here's the number, and here's exactly how we got it and where it can be wrong" is not just good engineering hygiene — for a service asking people to trust a map with a housing decision, it's the whole pitch.&lt;/p&gt;

&lt;p&gt;— austriano&lt;/p&gt;

</description>
      <category>gis</category>
      <category>python</category>
      <category>algorithms</category>
      <category>showdev</category>
    </item>
    <item>
      <title>영어 면접, 답이 안 떠올라도 읽으면 되는 연습 도구 (베타 무료)</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Sat, 22 Aug 2026 16:36:12 +0000</pubDate>
      <link>https://dev.to/tarofortune/yeongeo-myeonjeob-dabi-an-ddeoolrado-ilgeumyeon-doeneun-yeonseub-dogu-beta-muryo-43gj</link>
      <guid>https://dev.to/tarofortune/yeongeo-myeonjeob-dabi-an-ddeoolrado-ilgeumyeon-doeneun-yeonseub-dogu-beta-muryo-43gj</guid>
      <description>&lt;p&gt;영어 면접에서 제일 무서운 건 어려운 질문이 아니라, 질문 듣고 3초간 머리가 하얘지는 순간이잖아요.&lt;/p&gt;

&lt;p&gt;그래서 만든 도구입니다. AI 면접관이 영어로 질문하면, 내 이력서 기반 영어 답변이 화면에 뜨는데 문장마다 밑에 한글 발음이 달립니다.&lt;/p&gt;

&lt;p&gt;As a solo builder → 애즈 어 솔로 빌더&lt;/p&gt;

&lt;p&gt;안 떠올라도 그냥 읽으면 됩니다. 몇 번 돌리면 입에 붙어요.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI 모의면접: 음성 질문 + 답변 타이머 + 10초 카운트다운 + 한국어 코치 피드백&lt;/li&gt;
&lt;li&gt;예상질문 목록 넣으면 순서대로, 끝나면 AI 꼬리질문&lt;/li&gt;
&lt;li&gt;이력서·대화는 브라우저에만 저장 (본인 API 키로 직접 호출, 서버 미경유)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;단점: 본인 Claude/OpenAI API 키를 발급받아야 해서(무료, 5분) 처음엔 좀 귀찮습니다.&lt;/p&gt;

&lt;p&gt;베타 기간 전부 무료: &lt;a href="https://geoinfomatic.pythonanywhere.com/interview?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://geoinfomatic.pythonanywhere.com/interview?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>영어면접</category>
      <category>면접준비</category>
      <category>취업</category>
      <category>외국계</category>
    </item>
    <item>
      <title>Why I built a real-estate tool with zero real-estate listings</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Fri, 21 Aug 2026 16:33:13 +0000</pubDate>
      <link>https://dev.to/tarofortune/why-i-built-a-real-estate-tool-with-zero-real-estate-listings-1lmn</link>
      <guid>https://dev.to/tarofortune/why-i-built-a-real-estate-tool-with-zero-real-estate-listings-1lmn</guid>
      <description>&lt;p&gt;Every property site in Korea (Hogang-no-no, Zigbang, Dabang) shows the same listings with the same agents.&lt;/p&gt;

&lt;p&gt;What people actually need when moving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How far is the subway? (door to platform)&lt;/li&gt;
&lt;li&gt;Can my kid walk to school?&lt;/li&gt;
&lt;li&gt;Is there a mart within 10 min on foot?&lt;/li&gt;
&lt;li&gt;How long to my workplace, with transfers?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's what I built: &lt;a href="https://geoinfomatic.pythonanywhere.com?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://geoinfomatic.pythonanywhere.com?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No listings. No agents. Just an honest accessibility map.&lt;/p&gt;

&lt;p&gt;Funded by 5 free analyses/day + 9,900 KRW Pro tier. Built solo with Flask + Leaflet.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>civictech</category>
      <category>leaflet</category>
      <category>showdev</category>
    </item>
    <item>
      <title>PWA with React + Leaflet: lessons from a 31-city map app</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Thu, 20 Aug 2026 16:41:54 +0000</pubDate>
      <link>https://dev.to/tarofortune/pwa-with-react-leaflet-lessons-from-a-31-city-map-app-3eed</link>
      <guid>https://dev.to/tarofortune/pwa-with-react-leaflet-lessons-from-a-31-city-map-app-3eed</guid>
      <description>&lt;p&gt;Notes from shipping a real-world PWA in Korea:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vite SSG is worth it. SPAs lose Korean SEO because Naver's crawler doesn't fully render JS.&lt;/li&gt;
&lt;li&gt;Leaflet &amp;gt; Mapbox for cost-sensitive projects. Free OSM tiles + zero cost.&lt;/li&gt;
&lt;li&gt;Firebase Hosting rewrites let you have deep URLs (/city/suwon) without a backend.&lt;/li&gt;
&lt;li&gt;KakaoTalk share is non-negotiable in Korea — 70%+ of social traffic.&lt;/li&gt;
&lt;li&gt;PWA install banner: don't auto-prompt, let the user discover.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Project: &lt;a href="https://gyeonggi-currency-map.web.app?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://gyeonggi-currency-map.web.app?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>pwa</category>
      <category>react</category>
      <category>webdev</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Simulating winter sunlight for 38,000 apartment complexes (terrain + building shadows)</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Wed, 19 Aug 2026 17:56:12 +0000</pubDate>
      <link>https://dev.to/tarofortune/simulating-winter-sunlight-for-38000-apartment-complexes-terrain-building-shadows-2ip1</link>
      <guid>https://dev.to/tarofortune/simulating-winter-sunlight-for-38000-apartment-complexes-terrain-building-shadows-2ip1</guid>
      <description>&lt;p&gt;In Korea, "how much winter sun does this apartment get?" is a real buying question, not a nice-to-have. And the honest answer isn't "look at the balcony direction" — it's a geometry problem with three moving parts: where the sun actually is in the sky in December, what hills and mountains sit on the horizon, and whether the building directly in front casts a shadow over you.&lt;/p&gt;

&lt;p&gt;I built ZipScope (집스코프), a Korean living-area analysis map that scores 38,000+ apartment, officetel, and low-rise complexes nationwide across a seven-dimension living score — transit, schools, amenities, healthcare, environment, culture, and future value. Alongside that score, it also runs a set of terrain-based estimates: winter daylight, flood risk, and waterfront views. This post is about how the winter-sunlight simulation works. You can poke at the live version here: &lt;a href="https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://tarofortune.pythonanywhere.com/analyze?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the winter solstice is the only day that matters
&lt;/h2&gt;

&lt;p&gt;If you want a single worst-case number for daylight, you compute it on the winter solstice (동지). That's when the sun's arc across the sky is at its lowest and shortest for the whole year. A unit that gets decent light on the solstice gets at least that much every other day. A unit that's already blocked on the solstice is the one you warn people about.&lt;/p&gt;

&lt;p&gt;So the whole simulation is anchored to one day: trace the sun's path from sunrise to sunset on the solstice, and at each moment ask a single yes/no question — &lt;em&gt;can this complex actually see the sun right now, or is something in the way?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: the sun's winter arc
&lt;/h2&gt;

&lt;p&gt;The sun's position for any instant is a textbook astronomy calculation. Two angles describe it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;elevation&lt;/strong&gt; — how high above the horizon the sun is (0° at the horizon, 90° straight up)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;azimuth&lt;/strong&gt; — which compass direction it's in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the solstice the sun's declination bottoms out near −23.4°, so in Korean latitudes the noon sun never climbs very high — it skims low across the southern sky. That low arc is exactly why front buildings and terrain matter so much in winter: the sun is coming in at a shallow angle, so even a modest obstruction reaches up and clips it.&lt;/p&gt;

&lt;p&gt;Stepping through the day in small time increments gives a sequence of (azimuth, elevation) pairs — the sun's track. That's the thing everything else gets tested against.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: the terrain horizon from a DEM
&lt;/h2&gt;

&lt;p&gt;The sky isn't a flat 0° horizon. If there's a ridge to the south-east, the sun doesn't "rise" for that location until it clears the ridge. To capture this I use SRTM 30 m satellite DEM (digital elevation model) data.&lt;/p&gt;

&lt;p&gt;For a given complex I sample the DEM outward along many compass directions. In each direction I walk away from the point and compute the angle up to each terrain sample:&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;# horizon angle in one azimuth direction
&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;distances_along_ray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;dz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;elevation_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;elevation_here&lt;/span&gt;
    &lt;span class="n"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atan2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# elevation angle to that terrain point
&lt;/span&gt;    &lt;span class="n"&gt;horizon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;horizon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# keep the highest blocker
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do that for every direction and you get a &lt;strong&gt;horizon profile&lt;/strong&gt;: the minimum sun elevation needed to be visible in each azimuth. A location down in a valley has a high horizon profile; one on a hillside facing south has a low one. Sitting on the DEM also means every complex gets an honest elevation-above-sea-level readout as a by-product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: the front-building shadow angle
&lt;/h2&gt;

&lt;p&gt;Terrain explains the macro picture; the building directly in front (앞동) explains why apartment 3F is dark at noon while 15F is bright. A facing block subtends a shadow angle that depends on how tall it is and how far away it stands — and because winter sun comes in low, a fairly ordinary front building can block a surprising amount of it.&lt;/p&gt;

&lt;p&gt;The test is the same shape as the terrain one: the front structure contributes its own elevation angle in the azimuth range it occupies. Combine it with the terrain horizon by simply taking the higher blocker in each direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;The visibility test for every sampled moment of the solstice day becomes one comparison:&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;sunny_minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elev&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sun_track&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;            &lt;span class="c1"&gt;# sun path across the solstice
&lt;/span&gt;    &lt;span class="n"&gt;blocker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;terrain_horizon&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;# hills / mountains
&lt;/span&gt;                  &lt;span class="n"&gt;building_shadow&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;az&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# the block in front
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;elev&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;blocker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sunny_minutes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;step_minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sum the minutes where the sun clears every obstruction and you have a defensible winter-daylight estimate — not a vibe, a traced sun path checked against real terrain and real geometry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it 38,000 times
&lt;/h2&gt;

&lt;p&gt;Running this per complex, on demand, would be slow, so the heavy geometry is precomputed and the results stored. The stack is deliberately boring and cheap: Flask + React + Leaflet on the front, SQLite for storage, SRTM DEM for terrain and OSM Overpass for map features, all hosted on PythonAnywhere. Boring is a feature — it keeps the whole thing free to run for a nationwide dataset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honesty as a design constraint
&lt;/h2&gt;

&lt;p&gt;One rule I hold across the whole product: every estimate ships with its source and its limits. The sunlight figure is a &lt;em&gt;simulation&lt;/em&gt;, not a survey — a solstice-anchored model built from a 30 m DEM and building geometry, and it's labeled that way in the UI. Same discipline applies elsewhere in the app (the flood-risk layer, for instance, is clearly marked as an estimate and points users to the official flood map rather than pretending to replace it).&lt;/p&gt;

&lt;p&gt;It turns out "here's the number, and here's exactly how we got it and where it can be wrong" is not just good engineering hygiene — for a service asking people to trust a map with a housing decision, it's the whole pitch.&lt;/p&gt;

&lt;p&gt;— austriano&lt;/p&gt;

</description>
      <category>gis</category>
      <category>python</category>
      <category>algorithms</category>
      <category>showdev</category>
    </item>
    <item>
      <title>영어 면접, 답이 안 떠올라도 읽으면 되는 연습 도구 (베타 무료)</title>
      <dc:creator>TaroFortune</dc:creator>
      <pubDate>Mon, 17 Aug 2026 16:58:11 +0000</pubDate>
      <link>https://dev.to/tarofortune/yeongeo-myeonjeob-dabi-an-ddeoolrado-ilgeumyeon-doeneun-yeonseub-dogu-beta-muryo-5edm</link>
      <guid>https://dev.to/tarofortune/yeongeo-myeonjeob-dabi-an-ddeoolrado-ilgeumyeon-doeneun-yeonseub-dogu-beta-muryo-5edm</guid>
      <description>&lt;p&gt;영어 면접에서 제일 무서운 건 어려운 질문이 아니라, 질문 듣고 3초간 머리가 하얘지는 순간이잖아요.&lt;/p&gt;

&lt;p&gt;그래서 만든 도구입니다. AI 면접관이 영어로 질문하면, 내 이력서 기반 영어 답변이 화면에 뜨는데 문장마다 밑에 한글 발음이 달립니다.&lt;/p&gt;

&lt;p&gt;As a solo builder → 애즈 어 솔로 빌더&lt;/p&gt;

&lt;p&gt;안 떠올라도 그냥 읽으면 됩니다. 몇 번 돌리면 입에 붙어요.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI 모의면접: 음성 질문 + 답변 타이머 + 10초 카운트다운 + 한국어 코치 피드백&lt;/li&gt;
&lt;li&gt;예상질문 목록 넣으면 순서대로, 끝나면 AI 꼬리질문&lt;/li&gt;
&lt;li&gt;이력서·대화는 브라우저에만 저장 (본인 API 키로 직접 호출, 서버 미경유)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;단점: 본인 Claude/OpenAI API 키를 발급받아야 해서(무료, 5분) 처음엔 좀 귀찮습니다.&lt;/p&gt;

&lt;p&gt;베타 기간 전부 무료: &lt;a href="https://geoinfomatic.pythonanywhere.com/interview?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot" rel="noopener noreferrer"&gt;https://geoinfomatic.pythonanywhere.com/interview?ref=devto&amp;amp;utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=multi-site-bot&lt;/a&gt;&lt;/p&gt;

</description>
      <category>영어면접</category>
      <category>면접준비</category>
      <category>취업</category>
      <category>외국계</category>
    </item>
  </channel>
</rss>
