diff --git a/.env.dist b/.env.dist
index 6d526f32..4caf09f1 100644
--- a/.env.dist
+++ b/.env.dist
@@ -28,6 +28,7 @@ TWITTER_CLIENT_SECRET=ThisTokenIsNotSoSecretChangeIt
YOURLS_API_URL=http://sqi.be/yourls-api.php
YOURLS_API_USERNAME=username
YOURLS_API_PASSWORD=password
+STATICMAPS_HOST=https://maps.caldera.cc/
ADMIN_PASSWORD=123
diff --git a/config/services.yaml b/config/services.yaml
index 6da63814..34e2c355 100644
--- a/config/services.yaml
+++ b/config/services.yaml
@@ -7,6 +7,7 @@ parameters:
yourls.api_url: '%env(YOURLS_HOSTNAME)%'
yourls.api_username: '%env(YOURLS_USERNAME)%'
yourls.api_password: '%env(YOURLS_PASSWORD)%'
+ staticmaps.host: '%env(STATICMAPS_HOST)%'
assets_version: '%env(ASSETS_VERSION)%'
router.request_context.host: 'luft.jetzt'
router.request_context.scheme: 'https'
@@ -27,6 +28,7 @@ services:
$apiPassword: '%env(YOURLS_API_PASSWORD)%'
$assetsVersion: '%assets_version%'
$graphCacheDirectory: '%env(GRAPH_CACHE_DIRECTORY)%'
+ $staticmapsHost: '%staticmaps.host%'
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
@@ -43,6 +45,11 @@ services:
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
+ Sonata\SeoBundle\Seo\SeoPageInterface:
+ alias: sonata.seo.page.default
+
+ App\StationLoader\StationLoader: ~
+
App\Twitter\MessageFactory\MessageFactoryInterface:
alias: App\Twitter\MessageFactory\ExtendedEmojiMessageFactory
@@ -83,4 +90,4 @@ services:
$producer: '@old_sound_rabbit_mq.luft_value_producer'
App\Producer\Value\ValueProducerInterface:
- alias: App\Producer\Value\CacheValueProducer
\ No newline at end of file
+ alias: App\Producer\Value\CacheValueProducer
diff --git a/src/Controller/DisplayController.php b/src/Controller/DisplayController.php
index d7e005dc..5cadb9ce 100644
--- a/src/Controller/DisplayController.php
+++ b/src/Controller/DisplayController.php
@@ -16,6 +16,32 @@ class DisplayController extends AbstractController
{
public function indexAction(Request $request, SeoPage $seoPage, RequestConverterInterface $requestConverter, PollutionDataFactoryInterface $pollutionDataFactory, CityGuesserInterface $cityGuesser, Breadcrumbs $breadcrumbs, RouterInterface $router): Response
{
+ /** @var Station $station */
+ $station = $this->getDoctrine()->getRepository(Station::class)->findOneByStationCode($stationCode);
+
+ if (!$station) {
+ throw $this->createNotFoundException();
+ }
+
+ $boxList = $pollutionDataFactory->setCoord($station)->createDecoratedBoxList();
+
+ if ($station->getCity()) {
+ $seoPage->setTitle(sprintf('Luftmesswerte für die Station %s in %s', $station->getStationCode(), $station->getCity()->getName()));
+ } else {
+ $seoPage->setTitle(sprintf('Luftmesswerte für die Station %s', $station->getStationCode()));
+ }
+
+ $seoPage->setPreviewMap($station);
+
+ return $this->render('Default/station.html.twig', [
+ 'station' => $station,
+ 'boxList' => $boxList,
+ ]);
+ }
+
+ public function indexAction(Request $request, SeoPage $seoPage, PollutionDataFactory $pollutionDataFactory, StationFinderInterface $stationFinder): Response
+ {
+ $coord = $this->getCoordByRequest($request);
$coord = $requestConverter->getCoordByRequest($request);
if (!$coord) {
diff --git a/src/DependencyInjection/AppExtension.php b/src/DependencyInjection/AppExtension.php
new file mode 100644
index 00000000..1ecf556c
--- /dev/null
+++ b/src/DependencyInjection/AppExtension.php
@@ -0,0 +1,15 @@
+processConfiguration($configuration, $configs);
+ }
+}
diff --git a/src/DependencyInjection/Compiler/TwigSeoExtensionPass.php b/src/DependencyInjection/Compiler/TwigSeoExtensionPass.php
new file mode 100644
index 00000000..8432ac89
--- /dev/null
+++ b/src/DependencyInjection/Compiler/TwigSeoExtensionPass.php
@@ -0,0 +1,19 @@
+hasDefinition('sonata.seo.twig.extension')) {
+ return;
+ }
+
+ $container->removeDefinition('sonata.seo.twig.extension');
+ }
+}
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
new file mode 100644
index 00000000..e2913aaa
--- /dev/null
+++ b/src/DependencyInjection/Configuration.php
@@ -0,0 +1,17 @@
+root('caldera');
+
+ return $treeBuilder;
+ }
+}
diff --git a/src/Entity/City.php b/src/Entity/City.php
index aa068969..c6eb6557 100644
--- a/src/Entity/City.php
+++ b/src/Entity/City.php
@@ -2,6 +2,7 @@
namespace App\Entity;
+use App\StaticMap\StaticMapableInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
@@ -12,7 +13,7 @@
* @ORM\Table(name="city")
* @JMS\ExclusionPolicy("ALL")
*/
-class City
+class City implements StaticMapableInterface
{
/**
* @ORM\Id
@@ -46,6 +47,18 @@ class City
*/
protected $description;
+ /**
+ * @ORM\Column(type="float", nullable=false)
+ * @JMS\Expose()
+ */
+ protected $latitude;
+
+ /**
+ * @ORM\Column(type="float", nullable=false)
+ * @JMS\Expose()
+ */
+ protected $longitude;
+
/**
* @ORM\Column(type="string", nullable=true)
* @JMS\Expose()
@@ -93,6 +106,30 @@ public function setCreatedAt(\DateTime $createdAt): City
return $this;
}
+ public function getLatitude(): float
+ {
+ return $this->latitude;
+ }
+
+ public function setLatitude(float $latitude): City
+ {
+ $this->latitude = $latitude;
+
+ return $this;
+ }
+
+ public function getLongitude(): float
+ {
+ return $this->longitude;
+ }
+
+ public function setLongitude(float $longitude): City
+ {
+ $this->longitude = $longitude;
+
+ return $this;
+ }
+
public function getName(): ?string
{
return $this->name;
diff --git a/src/Entity/Station.php b/src/Entity/Station.php
index fc5abe1d..ab27d4e3 100644
--- a/src/Entity/Station.php
+++ b/src/Entity/Station.php
@@ -2,6 +2,7 @@
namespace App\Entity;
+use App\StaticMap\StaticMapableInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Caldera\GeoBasic\Coord\Coord;
@@ -17,7 +18,7 @@
* @UniqueEntity("stationCode")
* @JMS\ExclusionPolicy("ALL")
*/
-class Station extends Coord
+class Station extends Coord implements StaticMapableInterface
{
/**
* @ORM\Id
diff --git a/src/Kernel.php b/src/Kernel.php
index 89e091b6..18aae47f 100644
--- a/src/Kernel.php
+++ b/src/Kernel.php
@@ -2,6 +2,7 @@
namespace App;
+use App\DependencyInjection\Compiler\TwigSeoExtensionPass;
use App\Air\AirQuality\PollutionLevel\PollutionLevelInterface;
use App\Air\Measurement\MeasurementInterface;
use App\DependencyInjection\Compiler\PollutionLevelCompilerPass;
@@ -10,6 +11,7 @@
use App\DependencyInjection\Compiler\PollutantCompilerPass;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
+use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
@@ -54,6 +56,8 @@ protected function configureContainer(ContainerBuilder $container, LoaderInterfa
$loader->load($confDir.'/services/*'.self::CONFIG_EXTS, 'glob');
$loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob');
+ $container->addCompilerPass(new TwigSeoExtensionPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 100);
+
$container->addCompilerPass(new PollutionLevelCompilerPass());
$container->registerForAutoconfiguration(PollutionLevelInterface::class)->addTag('pollution_level');
diff --git a/src/Migrations/Version20181002172100.php b/src/Migrations/Version20181002172100.php
new file mode 100644
index 00000000..8ecdd3ff
--- /dev/null
+++ b/src/Migrations/Version20181002172100.php
@@ -0,0 +1,28 @@
+abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
+
+ $this->addSql('ALTER TABLE city ADD latitude DOUBLE PRECISION NOT NULL, ADD longitude DOUBLE PRECISION NOT NULL');
+ }
+
+ public function down(Schema $schema) : void
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
+
+ $this->addSql('ALTER TABLE city DROP latitude, DROP longitude');
+ }
+}
diff --git a/src/SeoPage/SeoPage.php b/src/SeoPage/SeoPage.php
index f29fdf69..d6a490c2 100644
--- a/src/SeoPage/SeoPage.php
+++ b/src/SeoPage/SeoPage.php
@@ -2,8 +2,23 @@
namespace App\SeoPage;
+use App\StaticMap\UrlGenerator\UrlGeneratorInterface;
+use Sonata\SeoBundle\Seo\SeoPageInterface;
+
class SeoPage extends AbstractSeoPage
{
+ /** @var SeoPageInterface */
+ protected $sonataSeoPage;
+
+ /** @var UrlGeneratorInterface $urlGenerator */
+ protected $urlGenerator;
+
+ public function __construct(SeoPageInterface $sonataSeoPage, UrlGeneratorInterface $urlGenerator)
+ {
+ $this->sonataSeoPage = $sonataSeoPage;
+ $this->urlGenerator = $urlGenerator;
+ }
+
public function setTitle(string $title): SeoPageInterface
{
$this->sonataSeoPage
@@ -32,6 +47,7 @@ public function setStandardPreviewPhoto(): SeoPageInterface
return $this;
}
+ public function setPreviewMap(StaticMapableInterface $staticMapable): SeoPage
public function setOpenGraphPreviewPhoto(string $assetUrl): SeoPageInterface
{
$this->sonataSeoPage->addMeta('property', 'og:image', $this->asset($assetUrl));
@@ -42,6 +58,11 @@ public function setOpenGraphPreviewPhoto(string $assetUrl): SeoPageInterface
public function setTwitterPreviewPhoto(string $assetUrl): SeoPageInterface
{
$this->sonataSeoPage
+ ->addMeta('property', 'og:image', $this->urlGenerator->generate($staticMapable, 600, 315),
+ ['escape' => false])
+ ->addMeta('name', 'twitter:image', $this->urlGenerator->generate($staticMapable, 800, 320),
+ ['escape' => false])
+ ->addMeta('name', 'twitter:card', 'summary_large_image');
->addMeta('name', 'twitter:image', $this->asset($assetUrl))
->addMeta('name', 'twitter:card', 'summary_large_image');
diff --git a/src/StaticMap/StaticMapableInterface.php b/src/StaticMap/StaticMapableInterface.php
new file mode 100644
index 00000000..21ec9f22
--- /dev/null
+++ b/src/StaticMap/StaticMapableInterface.php
@@ -0,0 +1,7 @@
+ '865x512',
+ ];
+
+ public function __construct(string $staticmapsHost)
+ {
+ $this->staticmapsHost = $staticmapsHost;
+ }
+}
diff --git a/src/StaticMap/UrlGenerator/UrlGenerator.php b/src/StaticMap/UrlGenerator/UrlGenerator.php
new file mode 100644
index 00000000..9816076e
--- /dev/null
+++ b/src/StaticMap/UrlGenerator/UrlGenerator.php
@@ -0,0 +1,67 @@
+staticmapsStation($object, $width, $height, $zoom);
+ } elseif ($object instanceof City) {
+ return $this->staticmapsCity($object, $width, $height, $zoom);
+ }
+
+ return '';
+ }
+
+ public function staticmapsCity(City $city, int $width = null, int $height = null, int $zoom = null): string
+ {
+ $parameters = [
+ 'markers' => sprintf('%f,%f,%s,%s,%s', $city->getLatitude(), $city->getLongitude(), 'circle', 'blue', 'university'),
+ ];
+
+ return $this->generateMapUrl($parameters, $width, $height, $zoom);
+ }
+
+ public function staticmapsStation(Station $station, int $width = null, int $height = null, int $zoom = null): string
+ {
+ $parameters = [
+ 'markers' => sprintf('%f,%f,%s,%s,%s', $station->getLatitude(), $station->getLongitude(), 'circle', 'blue', 'thermometer-full'),
+ ];
+
+ return $this->generateMapUrl($parameters, $width, $height, $zoom);
+ }
+
+ protected function generateMapUrl(array $parameters = [], int $width = null, int $height = null, int $zoom = null): string
+ {
+ $viewParameters = [];
+
+ if ($width && $height) {
+ $viewParameters['size'] = sprintf('%dx%d', $width, $height);
+ }
+
+ if ($zoom) {
+ $viewParameters['zoom'] = sprintf('%d', $zoom);
+ }
+
+ $parameters = array_merge($parameters, $this->defaultParameters, $viewParameters);
+
+ return sprintf('%sstaticmap.php?%s', $this->staticmapsHost, $this->generateMapUrlParameters($parameters));
+ }
+
+ protected function generateMapUrlParameters(array $parameters = []): string
+ {
+ $list = [];
+
+ foreach ($parameters as $key => $value) {
+ $list [] = sprintf('%s=%s', $key, $value);
+ }
+
+ return implode('&', $list);
+ }
+}
diff --git a/src/StaticMap/UrlGenerator/UrlGeneratorInterface.php b/src/StaticMap/UrlGenerator/UrlGeneratorInterface.php
new file mode 100644
index 00000000..77ee42d5
--- /dev/null
+++ b/src/StaticMap/UrlGenerator/UrlGeneratorInterface.php
@@ -0,0 +1,10 @@
+page->getMetas() as $type => $metas) {
+ foreach ((array) $metas as $name => $meta) {
+ list($content, $extras) = $meta;
+
+ if (!empty($content)) {
+ $html .= sprintf("\n",
+ $type,
+ $this->normalize($name),
+ array_key_exists('escape', $extras) && $extras['escape'] === false ? $content : $this->normalize($content)
+ );
+ } else {
+ $html .= sprintf("\n",
+ $type,
+ $this->normalize($name)
+ );
+ }
+ }
+ }
+
+ return $html;
+ }
+
+ private function normalize($string): string
+ {
+ return htmlentities(strip_tags($string), ENT_COMPAT, $this->encoding);
+ }
+}