weather, everywhere · drupal 8 is object oriented and drupal 7 is primarily procedural. this means...
TRANSCRIPT
Weather,
Weather,
EverywhereAdding a Weather Forecast to your pages
By Bradley Roberts
TW: @BE55Roberts
Awesome Hiking Experience
www.awesomehikingexperience.com
The ChallengeHow to encourage readers to go on trail hikes?
One Solution: Report the current weather forecast
Existing Modules for Weather
or Create My Own?
Drupal 8
Weather (only 7.x-2.14)
Weather Block (2014)
Wunderground (8.x-1.2)
Custom
Use GeoLocation
(lat,lng)
Support multiple places
Support trails
Basic Strategy: Add a Filter
Filter Content for Tag:
“
”
Drupal 8 is object oriented and Drupal 7 is
primarily procedural. This means that a lot of
your code will now be in classes rather than
simple functions. This is going to be
overwhelming for many, but will certainly
make module development more flexible and
you will upgrade your skills to modern
programming practice in the process.
By Blair Wadman at www.befused.com
Taking the Drupal 8 module approach will make your
custom module better in the long run.
/modules/custom/hiking_weather
Directory Structure
/images
/src
/src/Controller
/src/Plugin
Under Hiking Weather:
name: Hiking Weather
description: Shows the current weather on the hiking trail
package: Custom
type: module
core: 8.x
hiking_weather_info.yml
/**
* FILE: HikingWeatherController.php
* GOAL: To support Awesome Hiking
* AUTHOR: Bradley Roberts
* EMAIL: [email protected]
* DATE: 8/28/2017
*/
namespace Drupal\hiking_weather\Controller;
// Add Controller Base
use Drupal\Core\Controller\ControllerBase;
/**
* Class Hiking Weather Controller
* To process weather requests for specific hiking
locations
* @package Drupal\HikingWeather\Controller
*/
class HikingWeatherController extends ControllerBase {
}
HikingWeatherController.php
private $weatherTag = 'WEATHER';
private $weatherRegEx = '/\[(.*:\s)?(-?(?:\d+|\d*\.\d+))?,(-
?(?:\d+|\d*\.\d+))?\]/’;
HikingWeatherController.php
Use regex101.com to build your regular expression.
Properties:
Methods:
TIP:
public function content() {}
public function hiking_weather_node_view_alter() {}
public function parse_weather_tag() {}
public function getForecast() {}
hiking_weather.content:
path: '/hikingweather'
defaults:
_controller: '\Drupal\hiking_weather\Controller\HikingWeatherController::content'
_title: 'Hiking Weather'
requirements:
_permission: 'access content'
hiking_weather.routing.yml
hiking_weather.admin:
title: 'Hiking Weather Module Settings'
description: 'Settings for the Hiking Weather Module'
parent: system.admin_config_development
route_name: hiking_weather.content
weight: 100
hiking_weather.links.menu.yml
hiking_weather.theme.css.hiking {
font-weight: bold;
}
.hiking_forecast {
background-color: #cdd9fe;
float: left;
padding-right: 5px;
margin-right: 10px;
}
hiking_weather.libraries.yml
hiking-weather:
version: 1.x
css:
theme:
hiking_weather.theme.css: {}
Wunderground.com
Wunderground provides a Weather API
Free service for development and low usage
API Call
Weather Underground Map
Get Forecast API Call/**
* Get the Hiking Weather Forecast for a geolocation
*
* @param float $lat is the decimal latitude - e.g. 35.89
* @param float $lng is the decimal longitude - e.g. -83.94
*
* If not given, then defaults are used for home
*
* @return string rendering a DIV for the Forecast block
*/
public function getForecast($lat = 0.00, $lng = 0.00) {
$weather_api = 'http://api.wunderground.com/api’;
$api_key = APIKEY;
// Get the weather forecast from WonderGround.com
// EG: http://api.wunderground.com/api/APIKEY/geolookup/forecast/q/35.89583,-
83.9411.json
$json_string = file_get_contents($weather_api . '/' . $api_key .
'/geolookup/forecast/q/' . $lat . ',' . $lng . ".json");
$parsed_json = json_decode($json_string);
if ($parsed_json && (json_last_error() == JSON_ERROR_NONE)) {
$icon_url = (isset($parsed_json->forecast->simpleforecast->forecastday[0]-
>icon_url) ? $parsed_json->forecast->simpleforecast->forecastday[0]->icon_url :
$default_icon);
$conditions = (isset($parsed_json->forecast->simpleforecast->forecastday[0]-
>conditions) ? $parsed_json->forecast->simpleforecast->forecastday[0]->conditions :
$default_cond);
Get Forecast API Call// Map icon file to image on this system, since Drupal rejects Cross
Site Images
if (file_exists($images_dir . basename($icon_url))) {
$icon_img = $images_dir . basename($icon_url);
}
else {
$icon_img = $default_icon;
}
if (!$conditions || (strlen($conditions) == 0)) {
$conditions = $default_cond;
}
}
// Render DIV for the weather block with class hiking_forecast
return '<div><p class="hiking_forecast"><img src="' . $icon_img . '"
title="Weather Icon">' . $conditions . '</p><img src="' . $wuicon . '"
width="90px" title="Weather forecast courtesy of www.wunderground.com"></div>' .
PHP_EOL;
}
modules/custom/hiking_weather/src/Plugin/Filter/FilterArticle.php
/**
* FILE: FilterArticle.php
* GOAL: To support Awesome Hiking by filtering articles
* AUTHOR: Bradley Roberts
* EMAIL: [email protected]
* DATE: 09/01/2017
*/
namespace Drupal\hiking_weather\Plugin\Filter;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Class FilterArticle
* Looks for WEATHER tags to replace with a forecast object
*
* @package Drupal\hiking_weather\Plugin\Filter
*
* EG:
* @Filter(
* id = "filter_hiking_weather",
* title = @Translation("Hiking Weather Filter"),
* description = @Translation("Help potential hikers by providing forecast"),
* type = Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE,
* )
*/
class FilterArticle extends FilterBase {
/**
* Process the Filter Request
* @param string $text
* @param string $langcode
* @return \Drupal\filter\FilterProcessResult
modules/custom/hiking_weather/src/Plugin/Filter/FilterArticle.php
public function process($text, $langcode) {
// Instantiate the controller instance$weathering = new \Drupal\hiking_weather\Controller\HikingWeatherController();
// Identify the tag, latitude and longitude$geoTagged = $weathering->parse_weather_tag($text);
// Use API Call to retrieve the current forecast nearest this location$forecast = $weathering->getForecast($geoTagged['lat'], $geoTagged['lng']);
// Embed the rendered HTML into the markup$new_text = str_replace($geoTagged['tag'], $forecast, $text);
// Instantiate a filter process$result = new FilterProcessResult($new_text);
$result->setAttachments(array('library' => array('hiking_weather/hiking-weather'),
));
return $result;}
Rainbow Falls
Rainbow Falls Currently
<p>Current Weather on the Trail:</p>
<div>
<p class="hiking_forecast">
<img src="/modules/custom/hiking_weather/images/default_weather.gif" title="Weather Icon" />
Clear
</p>
<img src="/modules/custom/hiking_weather/images/wundergroundLogo_4c_horz.png" width="90px" title="Weather
forecast courtesy of www.wunderground.com" />
</div>
CAVEATS
• Weather is always changing, so you want to provide updates
• Caching should be limited to 1 hour to ensure up-to-date data
• Copy the Weather Icons to your server to customize them
• Wunderground limits the free, basic API to 10 calls/min
Weather Icon Sets:
https://www.wunderground.com/weather/api/d/docs?d=resources/icon-sets
CAVEATS
DEMO
QUESTIONS & ANSWERS
In Conclusion
www.awesomehikingexperience.com
Select a hike
Check the weather forecast for today!
Go hiking – you’ll love it
For more info about the Great Smoky Mountains:
Learning More About Drupal 8www.drupal.org/docs/8/creating-custom-modules
Lots of great information on all aspects of creating a module.
www.befused.com/drupal/first-drupal8-module
Step by step instructions for creating a D8 module.
https://youtu.be/EPjJQ7j3GaM
Drupal Camp Atlanta 2016 module on building a site for
CancerQuest.org
https://www.wunderground.com
Weather Underground Web Service. See about/data for details
on their data sources.