{"id":3140,"date":"2026-01-20T20:13:07","date_gmt":"2026-01-20T20:13:07","guid":{"rendered":"https:\/\/garyhengeveld.com\/wordpress\/?p=3140"},"modified":"2026-09-11T21:07:40","modified_gmt":"2026-09-11T21:07:40","slug":"doggie-park-locator","status":"publish","type":"post","link":"https:\/\/garyhengeveld.com\/wordpress\/doggie-park-locator\/","title":{"rendered":"Doggie Park Locator"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<title>Doggie Park Locator<\/title>\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet\/dist\/leaflet.css\" \/>\n<style>\n  body { margin: 0; font-family: Arial, sans-serif; background: #f7f7f7; }\n\n  h1 {\n    text-align: center;\n    margin: 15px 0 5px 0;\n    font-size: 32px;\n    color: #2b7a0b;\n    font-weight: 800;\n    letter-spacing: 1px;\n  }\n\n  #controls {\n    padding: 15px;\n    background: #fff;\n    box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n    display: flex;\n    gap: 10px;\n    align-items: center;\n    z-index: 1000;\n    position: relative;\n  }\n\n  #zip { padding: 10px; font-size: 16px; flex: 1; }\n\n  button {\n    padding: 10px 16px;\n    font-size: 16px;\n    background: #4CAF50;\n    color: #fff;\n    border: none;\n    cursor: pointer;\n  }\n\n  button:hover { background: #45a049; }\n\n  #status { margin-left: 10px; font-size: 14px; color: #333; }\n\n  #map { width: 100%; height: calc(100vh - 140px); }\n<\/style>\n<\/head>\n<body>\n\n<h1>Doggie Park Locator<\/h1>\n\n<div id=\"controls\">\n  <input id=\"zip\" type=\"text\" placeholder=\"Enter ZIP code\">\n  <button onclick=\"findDogParks()\">Search<\/button>\n  <span id=\"status\"><\/span>\n<\/div>\n\n<div id=\"map\"><\/div>\n\n<script src=\"https:\/\/unpkg.com\/leaflet\/dist\/leaflet.js\"><\/script>\n<script>\n\/\/ Initialize map\nlet map = L.map('map').setView([39.5, -98.35], 4);\nL.tileLayer('https:\/\/{s}.tile.openstreetmap.org\/{z}\/{x}\/{y}.png', { maxZoom: 19 }).addTo(map);\nlet markersLayer = L.layerGroup().addTo(map);\n\n\/\/ Cartoon-style green paw icon (reliable PNG)\nconst dogIconUrl = \"https:\/\/cdn-icons-png.flaticon.com\/512\/616\/616408.png\";\n\nconst iconDogGreen = L.icon({\n  iconUrl: dogIconUrl,\n  iconSize: [45, 45],\n  iconAnchor: [22, 22]\n});\n\n\/\/ Overpass fallback servers\nconst OVERPASS_SERVERS = [\n  \"https:\/\/overpass-api.de\/api\/interpreter\",\n  \"https:\/\/overpass.kumi.systems\/api\/interpreter\",\n  \"https:\/\/overpass.openstreetmap.fr\/api\/interpreter\"\n];\n\nasync function findDogParks() {\n  const zip = document.getElementById('zip').value.trim();\n  const status = document.getElementById('status');\n\n  if (!zip) {\n    status.textContent = \"Enter a ZIP code.\";\n    return;\n  }\n\n  status.textContent = \"Looking up ZIP\u2026\";\n\n  try {\n    \/\/ ZIP \u2192 coordinates\n    const zipRes = await fetch(`https:\/\/api.zippopotam.us\/us\/${zip}`);\n    if (!zipRes.ok) throw new Error(\"Invalid ZIP code\");\n    const zipData = await zipRes.json();\n\n    const lat = parseFloat(zipData.places[0].latitude);\n    const lon = parseFloat(zipData.places[0].longitude);\n\n    map.setView([lat, lon], 13);\n    status.textContent = \"Searching for dog parks\u2026\";\n\n    \/\/ Stable Overpass query: dog parks only\n    const query = `\n      [out:json][timeout:25];\n      (\n        node[\"leisure\"=\"dog_park\"](around:5000,${lat},${lon});\n        way[\"leisure\"=\"dog_park\"](around:5000,${lat},${lon});\n        relation[\"leisure\"=\"dog_park\"](around:5000,${lat},${lon});\n        node[\"amenity\"=\"dog_park\"](around:5000,${lat},${lon});\n        way[\"amenity\"=\"dog_park\"](around:5000,${lat},${lon});\n        relation[\"amenity\"=\"dog_park\"](around:5000,${lat},${lon});\n      );\n      out center;\n    `;\n\n    let data = null;\n\n    \/\/ Try servers in order until one works\n    for (const server of OVERPASS_SERVERS) {\n      try {\n        const res = await fetch(server, { method: \"POST\", body: query });\n        if (res.ok) {\n          data = await res.json();\n          break;\n        }\n      } catch (e) {}\n    }\n\n    if (!data) throw new Error(\"All Overpass servers failed\");\n\n    markersLayer.clearLayers();\n\n    if (!data.elements || data.elements.length === 0) {\n      status.textContent = \"No dog parks found nearby.\";\n      return;\n    }\n\n    status.textContent = `Found ${data.elements.length} dog parks`;\n\n    data.elements.forEach(el => {\n      const tags = el.tags || {};\n      const name = tags.name || \"Dog Park\";\n\n      const lat = el.lat || (el.center && el.center.lat);\n      const lon = el.lon || (el.center && el.center.lon);\n      if (!lat || !lon) return;\n\n      L.marker([lat, lon], { icon: iconDogGreen })\n        .addTo(markersLayer)\n        .bindPopup(`<strong>${name}<\/strong><br>Dog park`);\n    });\n\n  } catch (err) {\n    status.textContent = \"Error: \" + err.message;\n  }\n}\n<\/script>\n\n<\/body>\n<\/html>\n\n","protected":false},"excerpt":{"rendered":"<p>Doggie Park Locator Doggie Park Locator Search<\/p>\n","protected":false},"author":1,"featured_media":3154,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27,106],"tags":[],"class_list":["post-3140","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pets","category-software"],"_links":{"self":[{"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/posts\/3140","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/comments?post=3140"}],"version-history":[{"count":1,"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/posts\/3140\/revisions"}],"predecessor-version":[{"id":3141,"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/posts\/3140\/revisions\/3141"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/media\/3154"}],"wp:attachment":[{"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/media?parent=3140"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/categories?post=3140"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/garyhengeveld.com\/wordpress\/wp-json\/wp\/v2\/tags?post=3140"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}