Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HPC-9963: Replace leaflet with mapbox gl #1309

Merged
merged 1 commit into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .docksal/etc/apache/httpd-vhost-overrides.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<IfModule mod_rewrite.c>
RewriteEngine on
SSLProxyEngine on
#Add the string and keep hidden from user with [P]
# Add the string and keep hidden from user with [P]
RewriteCond %{QUERY_STRING} ^(([^&]*&)*)access_token=token(&.*)?$
RewriteRule ^/mapbox/(.*)$ https://api.mapbox.com/$1?%1access_token=${MAPBOX_TOKEN}%3 [P]
</IfModule>
5 changes: 0 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,6 @@
"drupal/webp": "^1.0-beta7",
"drush/drush": "^12.4",
"npm-asset/d3": "^7.4",
"npm-asset/leaflet": "^1.7",
"npm-asset/leaflet-modal": "^0.2.0",
"npm-asset/leaflet-providers": "^1.13",
"npm-asset/leaflet-search": "^3",
"npm-asset/leaflet-sidebar": "^0.2.2",
"npm-asset/select2": "^4.0",
"npm-asset/swiper": "^8.1",
"oomphinc/composer-installers-extender": "^2.0",
Expand Down
72 changes: 3 additions & 69 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions config/ghi_blocks.map_settings.yml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mapbox_proxy: true
country_outlines: false
6 changes: 3 additions & 3 deletions config/seckit.settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ seckit_xss:
webkit: false
report-only: true
default-src: "'none'"
script-src: "'self' 'unsafe-inline' https://js-agent.newrelic.com https://static.addtoany.com browser-update.org/update.min.js https://bam.nr-data.net cdn.jsdelivr.net/npm/toastify-js https://unpkg.com/@popperjs/core@2 https://unpkg.com/tippy.js@6"
script-src: "'self' 'unsafe-inline' https://js-agent.newrelic.com https://static.addtoany.com browser-update.org/update.min.js https://bam.nr-data.net cdn.jsdelivr.net/npm/toastify-js https://unpkg.com/@popperjs/core@2 https://unpkg.com/tippy.js@6 https://api.mapbox.com"
object-src: "'none'"
style-src: "'self' 'unsafe-inline' fonts.googleapis.com cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css https://fonts.gstatic.com"
style-src: "'self' 'unsafe-inline' fonts.googleapis.com cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css https://fonts.gstatic.com https://api.mapbox.com"
img-src: "'self' unocha.org hpc.tools content.hpc.tools fts.unocha.org data: api.mapbox.com browser-update.org https://fonts.gstatic.com"
media-src: ''
frame-src: "'self' https://content.hpc.tools https://www.youtube.com https://data.humdata.org https://datawrapper.dwcdn.net"
frame-ancestors: "'none'"
child-src: "'self'"
font-src: "'self' fonts.gstatic.com data:"
connect-src: "'self' https://bam.nr-data.net"
connect-src: "'self' https://bam.nr-data.net https://api.mapbox.com https://events.mapbox.com"
report-uri: /report-csp-violation
upgrade-req: false
policy-uri: ''
Expand Down

Large diffs are not rendered by default.

261 changes: 261 additions & 0 deletions html/modules/custom/ghi_base_objects/assets/geojson/countries.geojson

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions html/modules/custom/ghi_base_objects/ghi_base_objects.deploy.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,22 @@ function ghi_base_objects_deploy_base_object_bundles_admin_menu_2() {
])->save();
}
}

/**
* Import country outlines from the fallback source.
*/
function ghi_base_objects_deploy_import_country_outlines_from_fallback(&$sandbox) {
/** @var \Drupal\hpc_api\Query\EndpointQueryManager $endpoint_query_manager */
$endpoint_query_manager = \Drupal::service('plugin.manager.endpoint_query_manager');
/** @var \Drupal\ghi_base_objects\Plugin\EndpointQuery\CountryQuery $country_query */
$country_query = $endpoint_query_manager->createInstance('country_query');
$countries = $country_query->getCountries();
foreach ($countries as $country) {
\Drupal::queue('ghi_base_objects_download_country_geojson')->createItem((object) [
'country_id' => $country->id(),
]);
}
return (string) t('Enqueued @total countries for downloading their GeoJson country outline.', [
'@total' => \Drupal::queue('ghi_base_objects_download_country_geojson')->numberOfItems(),
]);
}
124 changes: 118 additions & 6 deletions html/modules/custom/ghi_base_objects/src/ApiObjects/Location.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,42 @@
*/
class Location extends BaseObject {

/**
* Define the paths to the fallback files for geojson country data.
*
* The paths are relative to the module directory.
*
* The UN dataset which unfortunately has quite some issues and renders a lot
* of artefacts. It comes from:
* https://geoportal.un.org/arcgis/apps/sites/#/geohub/datasets/d7caaff3ef4b4f7c82689b7c4694ad92/about.
*/
const GEOJSON_FALLBACK_FILE_UN = 'assets/geojson/UN_Geodata_simplified.geojson';

/**
* An alternative source.
*
* This comes from
* https://github.com/datasets/geo-countries via
* https://datahub.io/core/geo-countries and
* https://www.naturalearthdata.com/downloads/10m-cultural-vectors/.
*/
const GEOJSON_FALLBACK_FILE_OTHER = 'assets/geojson/countries.geojson';

/**
* {@inheritdoc}
*/
protected function map() {
$data = $this->getRawData();
return (object) [
'location_id' => $data->id,
'location_name' => $data->name,
'location_name' => $data->name ?: 'Admin area ' . $data->externalId,
'admin_level' => $data->adminLevel,
'pcode' => $data->pcode,
'iso3' => $data->iso3,
'latLng' => [(string) $data->latitude, (string) $data->longitude],
'filepath' => !empty($data->filepath) ? $data->filepath : NULL,
'parent_id' => $data->parentId,
'children' => $data->children ?? [],
];
}

Expand All @@ -34,8 +57,24 @@ protected function map() {
*/
public function getGeoJsonLocalFilePath($refresh = FALSE) {
$geojson_service = self::getGeoJsonService();
$uri = $geojson_service->getGeoJsonLocalFilePath($this->filepath, $refresh);
return $uri ? \Drupal::service('file_url_generator')->generate($uri)->toString() : NULL;
$file_url_generator = self::fileUrlGenerator();
if ($this->filepath && $uri = $geojson_service->getGeoJsonLocalFilePath($this->filepath, $refresh)) {
// If we have a filepath, let's point to it. This comes from the API and
// we store local copies of it.
return $uri ? $file_url_generator->generate($uri)->toString() : NULL;
}
if (!$this->iso3) {
return NULL;
}
// Otherwise let's see if we can get another type of local file that is
// extracted from a static geojson source and fetched via
// self::getGeoJsonFallback().
$local_filename = $this->iso3 . '.json';
if (!$geojson_service->localFileExists($local_filename)) {
$this->getGeoJsonFallback();
}
$filepath = $geojson_service->getLocalFilePath($local_filename);
return $filepath ? $file_url_generator->generate($filepath)->toString() : NULL;
}

/**
Expand All @@ -44,12 +83,65 @@ public function getGeoJsonLocalFilePath($refresh = FALSE) {
* @param bool $refresh
* Whether to refresh stored data.
*
* @return object
* The geo json data object.
* @return object|false
* The geo json data object or FALSE.
*/
public function getGeoJson($refresh = FALSE) {
$geojson_service = self::getGeoJsonService();
return $geojson_service->getGeoJson($this->filepath, $refresh);
$geojson = $this->filepath ? $geojson_service->getGeoJson($this->filepath, $refresh) : FALSE;
if (!$geojson) {
$geojson = $this->getGeoJsonFallback();
}
return $geojson;
}

/**
* Use a fallback to retrieve geojson polygon data for a location.
*
* @return object|false
* The geo json data object or FALSE.
*/
private function getGeoJsonFallback() {
if ($this->admin_level > 0) {
// The fallback is available only for admin level 0 locations.
return FALSE;
}
$geojson_service = self::getGeoJsonService();
$local_filename = $this->iso3 . '.json';
if ($geojson_service->localFileExists($local_filename)) {
return $geojson_service->getLocalFileContent($local_filename);
}

$geojson_file = self::moduleHandler()->getModule('ghi_base_objects')->getPath() . '/' . self::GEOJSON_FALLBACK_FILE_OTHER;
if (!file_exists($geojson_file)) {
return FALSE;
}
// Extract the features for the current location based on the iso3 code.
$content = json_decode(file_get_contents($geojson_file));
$features = array_filter($content->features, function ($item) {
return property_exists($item->properties, 'iso3cd') && $item->properties->iso3cd == $this->iso3 || property_exists($item->properties, 'ISO_A3') && $item->properties->ISO_A3 == $this->iso3;
});
if (empty($features)) {
return FALSE;
}
$features = array_values(array_map(function ($feature) {
unset($feature->properties);
return $feature;
}, $features));
$geojson = (object) [
'type' => 'Feature',
'geometry' => (object) [
'type' => 'GeometryCollection',
'geometries' => array_map(function ($feature) {
return $feature->geometry;
}, $features),
],
'properties' => (object) [
'location_id' => $this->id(),
],
];
$geojson_service->writeGeoJsonFile($local_filename, json_encode($geojson));
return $geojson;
}

/**
Expand All @@ -71,4 +163,24 @@ public static function getGeoJsonService() {
return \Drupal::service('hpc_api.geojson');
}

/**
* Get the file url generator service.
*
* @return \Drupal\Core\File\FileUrlGeneratorInterface
* The file url generator service.
*/
public static function fileUrlGenerator() {
return \Drupal::service('file_url_generator');
}

/**
* Get the module handler service.
*
* @return \Drupal\Core\Extension\ModuleHandlerInterface
* The module handler service.
*/
public static function moduleHandler() {
return \Drupal::service('module_handler');
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Drupal\ghi_base_objects\Plugin\EndpointQuery;

use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\ghi_base_objects\ApiObjects\Location;
use Drupal\hpc_api\Query\EndpointQueryBase;

/**
* Provides a query plugin for country locations.
*
* @EndpointQuery(
* id = "country_query",
* label = @Translation("Country query"),
* endpoint = {
* "api_key" = "location",
* "version" = "v2",
* }
* )
*/
class CountryQuery extends EndpointQueryBase {

use StringTranslationTrait;

/**
* Get country location objects for all countries.
*
* @return \Drupal\ghi_base_objects\ApiObjects\Location[]
* An array of country locations.
*/
public function getCountries() {
$cache_key = 'locations';
$countries = $this->cache($cache_key);
if ($countries) {
return $countries;
}
$data = $this->getData();
if (empty($data)) {
return [];
}

$countries = [];
foreach ($data as $item) {
$countries[$item->id] = new Location($item);
}
$this->cache($cache_key, $countries);
return $countries;
}

/**
* Get country location objects for all countries.
*
* @return \Drupal\ghi_base_objects\ApiObjects\Location|null
* A location object or NULL.
*/
public function getCountry($country_id) {
$countries = $this->getCountries();
return array_key_exists($country_id, $countries) ? $countries[$country_id] : NULL;
}

}
Loading
Loading