<main>
  <h1>Map &amp; Weather From IP</h1>

<section class="mainContainer">
  <div id="map"></div><!-- map -->
  <div id="details"></div><!-- details -->
</section>
</main>

/* NOTES

Please make sure to check CSS settings to see the external libraries loaded here, such as Open Props
https://open-props.style
*/

@import url("https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=Maven+Pro:wght@400;700&family=Oswald:wght@200;700&display=swap");

:root {
  --main-color1: var(--teal-9);
  --main-color2: var(--gray-9);
  --main-color3: #f0f4b5;

  --font1: "Maven Pro", sans-serif;
  --font2: "DM Serif Display", sans-serif;

  --mainBG: linear-gradient(
    to right top,
    var(--teal-9),
    #004d7a,
    #008793,
    #00bf72,
    #a8eb12
  );

  --radius-value-1: 1.25rem;
  --radius-value-2: 0px 0px .75rem .75rem;

  --box-shadow: rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px;
}

body {
  background-color: #000;
}

h1 {
  font-family: var(--font1);
  color: var(--teal-9);
  font-size: 2.6rem;
  font-weight: 700;
  margin: 0;
  padding: .3rem;
  text-align: center;
  text-shadow: -1px -1px 1px rgba(255,255,255,.1), 1px 1px 1px rgba(0,0,0,.5);
}

div.leaflet-popup-content h1 {
  color: var(--main-color2);
  font-size: 2.25rem;
  line-height: 2.5rem;
  font-weight: normal !important;
}

:where(h2, h3) {
  color: var(--main-color1);
  font-family: var(--font2);
  margin: 0;
  padding: 1rem;
}

h2 {
  color: white;
  padding: 0.5rem;
  margin: 0;
  background-color: rgba(0 0 0 /0.3);
   border-radius: var(--radius-value-1);
}

h3 {
  color: var(--main-color3);
  font-weight: 400;
}

h3:first-letter {
  text-transform: uppercase;
}


p {
  color: white;
  font-family: var(--font1);
  margin: 0.25rem;
  font-size: 1.15rem;
  line-height: 1.14rem;
}

div.leaflet-popup-content p {
  color: var(--main-color1);
    margin: 0;
  text-align: center;
}

div#currentWeather p,
div#details p {
  margin-bottom: .5rem;
}

p span.myDataHighlights
{
color: var(--main-color3);
}

main
{
margin: 0 auto;
}

section.mainContainer {
  margin-top: 1rem;
  display: grid;
  grid-template-columns: repeat(1, 1fr);
  grid-gap: 1rem;
}

/* div.leaflet-popup-content-wrapper
{
  margin: .5rem;
  background-color:rgba(255 255 255 /.64) ;
    position: relative;
  top: 10px!important;
}

.leaflet-popup-tip-container {

}

.leaflet-fade-anim .leaflet-map-pane .leaflet-popup
{
margin-top: 1rem;
} */


div#map {
  width: 100%;
  max-width: 90rem;
  height: 300px;
  border-radius: var(--radius-value-1);
}

div#currentConditionsHeader {

  margin-bottom: 2rem;
    border-radius: var(--radius-value-1);
  padding: .3rem;
  
    box-shadow: 0 2px 6px rgba(0,0,0,0.5), inset 0 1px rgba(255,255,255,0.3), inset 0 10px rgba(255,255,255,0.2), inset 0 10px 20px rgba(255,255,255,0.25), inset 0 -15px 30px rgba(0,0,0,0.3);
  
}

div#details {
  border: 1px solid rgba(0 0 0/1);
  padding: 1rem;
  width: 100%;
  max-width: 36rem;
  background-image: var(--mainBG);
  border-radius: var(--radius-value-1);
}

div.currentConditions {
  text-transform: capitalize;
}

img.myWeatherIcon {
  float: left;
  width: 100%;
  height: auto;
  max-width: 3rem;
    margin-top:0rem;
  margin-right: 1rem;
  margin-bottom: 0rem;
  border-radius: var(--radius-value);

  padding: .2rem;
  box-shadow: var(--box-shadow);
}


@media (min-width: 768px)
{
section.mainContainer {
  grid-template-columns: repeat(2, 1fr);
}
  
div#details {
  max-width: 48rem;
}
// NOTES

// Please make sure to check JS settings to see the external libraries loaded here, such as Leaflet
// https://leafletjs.com/

// What is a timestamp? Unix timestamps are the number of seconds elapsed since 01-01-1970 00:00:00 UTC - generally it should be equal everywhere.

// https://stackoverflow.com/questions/221294/how-do-i-get-a-timestamp-in-javascript

console.clear();

$(document).ready(function () {
  // run this function as soon as the page has completely loaded:
  drawMyMap(); // this function is created below
});

function myUNIXconverter(my_IP_data, ampm) {
  let unix_timestamp = my_IP_data;
  // Here we create a new JavaScript Date object based on the timestamp.
  // Pro tip: see how we multiply the timestamp by 1000 so that the argument is in milliseconds, not seconds:
  var date = new Date(unix_timestamp * 1000);

  // Year from the timestamp
  var my_year = date.getFullYear();

  // Hours from the timestamp
  var hours = date.getHours();

  // Gives us a 24-hour day with no 13 o'clock:
  if (hours > 11) {
    hours = hours - 12;
  }

  // Minutes from the timestamp
  var minutes = "0" + date.getMinutes();

  // Seconds from the timestamp
  var seconds = "0" + date.getSeconds();

  var formattedTime = hours + ":" + minutes.substr(-2); // Will display time in 10:30:23 format

  return formattedTime + ampm;
}

// /////////////////////
// Get My IP ADDRESS ///
// /////////////////////

// Here I use an external API which determines my IP address and related information about my location then returns it to me as JSON:

function drawMyMap(my_IP_data) {
  $.getJSON("https://ipapi.co/json/", function (my_IP_data) {
    console.log("Received data:", my_IP_data); // For testing

    // go through the received data and create variables as needed:

    var ip = my_IP_data.ip;
    var myLatitude = my_IP_data.latitude;
    var myLongitude = my_IP_data.longitude;
    var region = my_IP_data.region;
    var city = my_IP_data.city;
    var postal = my_IP_data.postal;
    var myZoom = "14";
    var myLatLong = [myLatitude, myLongitude];
    var map = L.map("map").setView(myLatLong, myZoom);

    // Here, I load "tiles" from a mapping API called Leaflet //
    L.tileLayer("https://{s}.tile.osm.org/{z}/{x}/{y}.png", {
      attribution: "&copy; JWC &nbsp;&nbsp;"
    }).addTo(map);

    L.marker(myLatLong)
      .addTo(map)
      .bindPopup(
        '<div id="currentWeather"></div><p>' + city + ", " + region + "</p>"
      )
      .openPopup();

    ///////////////////////
    // WEATHER ////////////
    ///////////////////////

    // Now I use a weather API: https://openweathermap.org/current#current_JSON
    // REMEMBER: you need to add your own (free) API key to get this to show the weather.
    // See: https://openweathermap.org/appid#:~:text=The%20API%20key%20is%20all,additional%20API%20keys%20if%20needed.%205c83aa0632e9e1a99ec3d9c6902ebc41

    var myKey = "5c83aa0632e9e1a99ec3d9c6902ebc41";
    var unit = "imperial";
    var weatherUrl =
      "https://api.openweathermap.org/data/2.5/weather?forecast&zip=" +
      postal +
      "&appid=" +
      myKey +
      "&units=" +
      unit;

    // If there *is* a zip code, made an AJAX call to the wetaher API and request data as jsonp:
    if (postal) {
      $.ajax({
        url: weatherUrl,
        type: "GET",
        dataType: "jsonp",
        success: function (my_weather_data) {
          var widget = show_the_weather(my_weather_data);
          $("#currentWeather").html(widget); // NOTE: "currentWeather" is a div created in the map widget, above
        }
      });
    }
  });
}

// Get the weather values from the returned weather data array: 
function show_the_weather(my_weather_data) {
  var initialTemperature = Math.round(my_weather_data.main.temp);
  var feels_like = Math.round(my_weather_data.main.feels_like);
  var temp_max = Math.round(my_weather_data.main.temp_max);
  var temp_min = Math.round(my_weather_data.main.temp_min);
  var humidity = Math.round(my_weather_data.main.humidity);
  var wind = Math.round(my_weather_data.wind.speed);
  var wind_direction = Math.round(my_weather_data.wind.deg);
  var wind_gust = Math.round(my_weather_data.wind.gust);
  console.log(wind_gust);

  let wind_gust_text = "";
  if (wind_gust) {
    wind_gust_text =
      ', gusting to <span class="myDataHighlights">' + wind_gust + "</span>";
  } else {
    wind_gust_text = "";
  }

  // https://uni.edu/storm/Wind%20Direction%20slide.pdf:
  if (wind_direction == 150 || wind_direction == 160) {
    wind_direction = "SE";
  } else if (
    wind_direction == 350 ||
    wind_direction == 360 ||
    wind_direction == 10
  ) {
    wind_direction = "N";
  } else if (wind_direction == 20 || wind_direction == 30) {
    wind_direction = "N NE";
  } else if (wind_direction == 40 || wind_direction == 50) {
    wind_direction = "NE";
  } else if (wind_direction == 60 || wind_direction == 70) {
    wind_direction = "E NE";
  } else if (
    wind_direction == 80 ||
    wind_direction == 90 ||
    wind_direction == 100
  ) {
    wind_direction = "E";
  } else if (wind_direction == 110 || wind_direction == 120) {
    wind_direction = "E SE";
  } else if (wind_direction == 130 || wind_direction == 140) {
    wind_direction = "SE";
  } else if (wind_direction == 150 || wind_direction == 160) {
    wind_direction = "S SE";
  } else if (
    wind_direction == 170 ||
    wind_direction == 180 ||
    wind_direction == 190
  ) {
    wind_direction = "S";
  } else if (wind_direction == 200 || wind_direction == 210) {
    wind_direction = "S SW";
  } else if (wind_direction == 220 || wind_direction == 230) {
    wind_direction = "SW";
  } else if (wind_direction == 240 || wind_direction == 250) {
    wind_direction = "W SW";
  } else if (
    wind_direction == 260 ||
    wind_direction == 270 ||
    wind_direction == 280
  ) {
    wind_direction = "W";
  } else if (wind_direction == 290 || wind_direction == 300) {
    wind_direction = "W NW";
  } else if (wind_direction == 310 || wind_direction == 320) {
    wind_direction = "NW";
  } else if (wind_direction == 330 || wind_direction == 340) {
    wind_direction = "N NW";
  } else {
    wind_direction = "";
  }

  var sunrise = myUNIXconverter(my_weather_data.sys.sunrise, "am");
  var sunset = myUNIXconverter(my_weather_data.sys.sunset, "pm");
  var currentConditions = my_weather_data.weather[0].main;
  var currentConditionLocation = my_weather_data.name;

  var currentConditionDescription = my_weather_data.weather[0].description;
  console.log(currentConditionDescription);
  
  switch (currentConditionDescription) {
    case "clear sky":
      var weatherConditionsIcon = "https://openweathermap.org/img/wn/01n.png";
      break;

    case "few clouds":
      var weatherConditionsIcon = "https://openweathermap.org/img/wn/02n.png";
      break;

    case "broken clouds":
      var weatherConditionsIcon = "https://openweathermap.org/img/wn/04n.png";
      break;

    case "scattered clouds":
    case "overcast clouds":
      var weatherConditionsIcon = "https://openweathermap.org/img/wn/03n.png";
      break;

    case "rain":
    case "shower rain":
    case "moderate rain":
    case "light rain":
      var weatherConditionsIcon =
        "https://openweathermap.org/img/wn/09n@2x.png";
      break;

    case "thunderstorm":
      var weatherConditionsIcon =
        "https://openweathermap.org/img/wn/11n@2x.png";
      break;

    case "snow":
      var weatherConditionsIcon =
        "https://openweathermap.org/img/wn/13n@2x.png";
      break;

    case "mist":
      var weatherConditionsIcon =
        "https://openweathermap.org/img/wn/50n@2x.png";
      break;

    default:
      var weatherConditionsIcon =
        "https://openweathermap.org/img/wn/50n@2x.png";
      break;
  }

  console.log(weatherConditionsIcon);
  
  // Use jQuery to populate the "details" div with our concatenated data:
  $("#details").html(
    '<div id="currentConditionsHeader">' +
      '<h2 class="currentConditions">' +
      currentConditions +
      "</h2>" +
      '<img src="' +
      weatherConditionsIcon +
      '" class="myWeatherIcon"/>' +
      '<h3 class="currentConditions">' +
      currentConditionDescription +
      " in the " +
      currentConditionLocation +
      "  area</h3></div>" +
      '<p>It\'s <span class="myDataHighlights"> ' +
      initialTemperature +
      '</span> degrees outside (it feels like <span class="myDataHighlights">' +
      feels_like +
      '</span>, due to <span class="myDataHighlights">' +
      humidity +
      "%</span> humidity.)</p>" +
      '<p>Wind is <span class="myDataHighlights">' +
      wind_direction +
      '</span> @ <span class="myDataHighlights">' +
      wind +
      " mph</span>" +
      wind_gust_text +
      ".</p>" +
      '<p>Today it\'s likely to get as high as <span class="myDataHighlights">' +
      temp_max +
      '</span>, with a low of <span class="myDataHighlights">' +
      temp_min +
      "</span>.</p>" +
      '<p>Sunrise @ <span class="myDataHighlights">' +
      sunrise +
      '</span>, sunset @ <span class="myDataHighlights">' +
      sunset +
      "</span>."
  );
  
  // write to the log so we can confirm our rec'd data
  console.log(my_weather_data);

  return "<h1><strong> " + initialTemperature + "°</strong></h1>";
}

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/modern-normalize/1.1.0/modern-normalize.min.css
  2. https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet.css
  3. https://unpkg.com/open-props

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet.js