Skip to content

Commit 93c9af6

Browse files
committed
HPC-9963: WIP Replace leaflet with mapbox gl
1 parent e06e0b6 commit 93c9af6

File tree

72 files changed

+5583
-5675
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+5583
-5675
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<IfModule mod_rewrite.c>
22
RewriteEngine on
33
SSLProxyEngine on
4-
#Add the string and keep hidden from user with [P]
4+
# Add the string and keep hidden from user with [P]
55
RewriteCond %{QUERY_STRING} ^(([^&]*&)*)access_token=token(&.*)?$
66
RewriteRule ^/mapbox/(.*)$ https://api.mapbox.com/$1?%1access_token=${MAPBOX_TOKEN}%3 [P]
77
</IfModule>

composer.json

-5
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,6 @@
133133
"drupal/webp": "^1.0-beta7",
134134
"drush/drush": "^12.4",
135135
"npm-asset/d3": "^7.4",
136-
"npm-asset/leaflet": "^1.7",
137-
"npm-asset/leaflet-modal": "^0.2.0",
138-
"npm-asset/leaflet-providers": "^1.13",
139-
"npm-asset/leaflet-search": "^3",
140-
"npm-asset/leaflet-sidebar": "^0.2.2",
141136
"npm-asset/select2": "^4.0",
142137
"npm-asset/swiper": "^8.1",
143138
"oomphinc/composer-installers-extender": "^2.0",

composer.lock

+1-67
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/seckit.settings.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,25 @@ seckit_xss:
88
webkit: false
99
report-only: true
1010
default-src: "'none'"
11-
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"
11+
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"
1212
object-src: "'none'"
13-
style-src: "'self' 'unsafe-inline' fonts.googleapis.com cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css https://fonts.gstatic.com"
13+
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"
1414
img-src: "'self' unocha.org hpc.tools content.hpc.tools fts.unocha.org data: api.mapbox.com browser-update.org https://fonts.gstatic.com"
1515
media-src: ''
1616
frame-src: "'self' https://content.hpc.tools https://www.youtube.com https://data.humdata.org https://datawrapper.dwcdn.net"
1717
frame-ancestors: "'none'"
1818
child-src: "'self'"
1919
font-src: "'self' fonts.gstatic.com data:"
20-
connect-src: "'self' https://bam.nr-data.net"
20+
connect-src: "'self' https://bam.nr-data.net https://api.mapbox.com https://events.mapbox.com"
2121
report-uri: /report-csp-violation
2222
upgrade-req: false
2323
policy-uri: ''
2424
x_xss:
25+
select: 0
2526
seckit_x_xss_option_disable: Disabled
2627
seckit_x_xss_option_0: '0'
2728
seckit_x_xss_option_1: 1;
2829
seckit_x_xss_option_1_block: '1; mode=block'
29-
select: 0
3030
seckit_csrf:
3131
origin: false
3232
origin_whitelist: ''

html/modules/custom/ghi_base_objects/assets/geojson/UN_Geodata_simplified.geojson

+1
Large diffs are not rendered by default.

html/modules/custom/ghi_base_objects/assets/geojson/countries.geojson

+261
Large diffs are not rendered by default.

html/modules/custom/ghi_base_objects/ghi_base_objects.deploy.php

+33
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,36 @@ function ghi_base_objects_deploy_base_object_bundles_admin_menu_2() {
3535
])->save();
3636
}
3737
}
38+
39+
/**
40+
* Import country outlines from the fallback source.
41+
*
42+
* @todo Make this use the queue api so that we can re-use it.
43+
*/
44+
function ghi_base_objects_deploy_import_country_outlines_from_fallback(&$sandbox) {
45+
set_time_limit(0);
46+
if (!array_key_exists('locations', $sandbox)) {
47+
/** @var \Drupal\hpc_api\Query\EndpointQueryManager $endpoint_query_manager */
48+
$endpoint_query_manager = \Drupal::service('plugin.manager.endpoint_query_manager');
49+
/** @var \Drupal\ghi_base_objects\Plugin\EndpointQuery\CountryQuery $country_query */
50+
$country_query = $endpoint_query_manager->createInstance('country_query');
51+
$sandbox['locations'] = $country_query->getCountries();
52+
$sandbox['total'] = count($sandbox['locations']);
53+
}
54+
55+
$location = array_shift($sandbox['locations']);
56+
$location->getGeoJson();
57+
58+
$sandbox['#finished'] = empty($sandbox['locations']);
59+
if ($sandbox['#finished']) {
60+
return (string) t('Imported geojson data for @total locations.', [
61+
'@total' => $sandbox['total'],
62+
]);
63+
}
64+
else {
65+
return (string) t('Importing geojson data for @current / @total locations.', [
66+
'@current' => $sandbox['total'] - count($sandbox['locations']),
67+
'@total' => $sandbox['total'],
68+
]);
69+
}
70+
}

html/modules/custom/ghi_base_objects/src/ApiObjects/Location.php

+115-6
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,42 @@
77
*/
88
class Location extends BaseObject {
99

10+
/**
11+
* Define the paths to the fallback files for geojson country data.
12+
*
13+
* The paths are relative to the module directory.
14+
*
15+
* The UN dataset which unfortunately has quite some issues and renders a lot
16+
* of artefacts. It comes from:
17+
* https://geoportal.un.org/arcgis/apps/sites/#/geohub/datasets/d7caaff3ef4b4f7c82689b7c4694ad92/about.
18+
*/
19+
const GEOJSON_FALLBACK_FILE_UN = 'assets/geojson/UN_Geodata_simplified.geojson';
20+
21+
/**
22+
* An alternative source.
23+
*
24+
* This comes from
25+
* https://github.com/datasets/geo-countries via
26+
* https://datahub.io/core/geo-countries and
27+
* https://www.naturalearthdata.com/downloads/10m-cultural-vectors/.
28+
*/
29+
const GEOJSON_FALLBACK_FILE_OTHER = 'assets/geojson/countries.geojson';
30+
1031
/**
1132
* {@inheritdoc}
1233
*/
1334
protected function map() {
1435
$data = $this->getRawData();
1536
return (object) [
1637
'location_id' => $data->id,
17-
'location_name' => $data->name,
38+
'location_name' => $data->name ?: 'Admin area ' . $data->externalId,
1839
'admin_level' => $data->adminLevel,
1940
'pcode' => $data->pcode,
41+
'iso3' => $data->iso3,
2042
'latLng' => [(string) $data->latitude, (string) $data->longitude],
2143
'filepath' => !empty($data->filepath) ? $data->filepath : NULL,
2244
'parent_id' => $data->parentId,
45+
'children' => $data->children ?? [],
2346
];
2447
}
2548

@@ -34,8 +57,21 @@ protected function map() {
3457
*/
3558
public function getGeoJsonLocalFilePath($refresh = FALSE) {
3659
$geojson_service = self::getGeoJsonService();
37-
$uri = $geojson_service->getGeoJsonLocalFilePath($this->filepath, $refresh);
38-
return $uri ? \Drupal::service('file_url_generator')->generate($uri)->toString() : NULL;
60+
$file_url_generator = self::fileUrlGenerator();
61+
if ($this->filepath && $uri = $geojson_service->getGeoJsonLocalFilePath($this->filepath, $refresh)) {
62+
// If we have a filepath, let's point to it. This comes from the API and
63+
// we store local copies of it.
64+
return $uri ? $file_url_generator->generate($uri)->toString() : NULL;
65+
}
66+
// Otherwise let's see if we can get another type of local file that is
67+
// extracted from a static geojson source and fetched via
68+
// self::getGeoJsonFallback().
69+
$local_filename = $this->iso3 . '.json';
70+
if (!$geojson_service->localFileExists($local_filename)) {
71+
$this->getGeoJsonFallback();
72+
}
73+
$filepath = $geojson_service->getLocalFilePath($local_filename);
74+
return $filepath ? $file_url_generator->generate($filepath)->toString() : NULL;
3975
}
4076

4177
/**
@@ -44,12 +80,65 @@ public function getGeoJsonLocalFilePath($refresh = FALSE) {
4480
* @param bool $refresh
4581
* Whether to refresh stored data.
4682
*
47-
* @return object
48-
* The geo json data object.
83+
* @return object|false
84+
* The geo json data object or FALSE.
4985
*/
5086
public function getGeoJson($refresh = FALSE) {
5187
$geojson_service = self::getGeoJsonService();
52-
return $geojson_service->getGeoJson($this->filepath, $refresh);
88+
$geojson = $this->filepath ? $geojson_service->getGeoJson($this->filepath, $refresh) : FALSE;
89+
if (!$geojson) {
90+
$geojson = $this->getGeoJsonFallback();
91+
}
92+
return $geojson;
93+
}
94+
95+
/**
96+
* Use a fallback to retrieve geojson polygon data for a location.
97+
*
98+
* @return object|false
99+
* The geo json data object or FALSE.
100+
*/
101+
private function getGeoJsonFallback() {
102+
if ($this->admin_level > 0) {
103+
// The fallback is available only for admin level 0 locations.
104+
return FALSE;
105+
}
106+
$geojson_service = self::getGeoJsonService();
107+
$local_filename = $this->iso3 . '.json';
108+
if ($geojson_service->localFileExists($local_filename)) {
109+
return $geojson_service->getLocalFileContent($local_filename);
110+
}
111+
112+
$geojson_file = self::moduleHandler()->getModule('ghi_base_objects')->getPath() . '/' . self::GEOJSON_FALLBACK_FILE_OTHER;
113+
if (!file_exists($geojson_file)) {
114+
return FALSE;
115+
}
116+
// Extract the features for the current location based on the iso3 code.
117+
$content = json_decode(file_get_contents($geojson_file));
118+
$features = array_filter($content->features, function ($item) {
119+
return property_exists($item->properties, 'iso3cd') && $item->properties->iso3cd == $this->iso3 || property_exists($item->properties, 'ISO_A3') && $item->properties->ISO_A3 == $this->iso3;
120+
});
121+
if (empty($features)) {
122+
return FALSE;
123+
}
124+
$features = array_values(array_map(function ($feature) {
125+
unset($feature->properties);
126+
return $feature;
127+
}, $features));
128+
$geojson = (object) [
129+
'type' => 'Feature',
130+
'geometry' => (object) [
131+
'type' => 'GeometryCollection',
132+
'geometries' => array_map(function ($feature) {
133+
return $feature->geometry;
134+
}, $features),
135+
],
136+
'properties' => (object) [
137+
'location_id' => $this->id(),
138+
],
139+
];
140+
$geojson_service->writeGeoJsonFile($local_filename, json_encode($geojson));
141+
return $geojson;
53142
}
54143

55144
/**
@@ -71,4 +160,24 @@ public static function getGeoJsonService() {
71160
return \Drupal::service('hpc_api.geojson');
72161
}
73162

163+
/**
164+
* Get the file url generator service.
165+
*
166+
* @return \Drupal\Core\File\FileUrlGeneratorInterface
167+
* The file url generator service.
168+
*/
169+
public static function fileUrlGenerator() {
170+
return \Drupal::service('file_url_generator');
171+
}
172+
173+
/**
174+
* Get the module handler service.
175+
*
176+
* @return \Drupal\Core\Extension\ModuleHandlerInterface
177+
* The module handler service.
178+
*/
179+
public static function moduleHandler() {
180+
return \Drupal::service('module_handler');
181+
}
182+
74183
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace Drupal\ghi_base_objects\Plugin\EndpointQuery;
4+
5+
use Drupal\Core\StringTranslation\StringTranslationTrait;
6+
use Drupal\ghi_base_objects\ApiObjects\Location;
7+
use Drupal\hpc_api\Query\EndpointQueryBase;
8+
9+
/**
10+
* Provides a query plugin for country locations.
11+
*
12+
* @EndpointQuery(
13+
* id = "country_query",
14+
* label = @Translation("Country query"),
15+
* endpoint = {
16+
* "api_key" = "location",
17+
* "version" = "v2",
18+
* }
19+
* )
20+
*/
21+
class CountryQuery extends EndpointQueryBase {
22+
23+
use StringTranslationTrait;
24+
25+
/**
26+
* Get country location objects for all countries.
27+
*
28+
* @return \Drupal\ghi_base_objects\ApiObjects\Location[]
29+
* An array of country locations.
30+
*/
31+
public function getCountries() {
32+
$cache_key = 'locations';
33+
$countries = $this->cache($cache_key);
34+
if ($countries) {
35+
return $countries;
36+
}
37+
$data = $this->getData();
38+
if (empty($data)) {
39+
return [];
40+
}
41+
42+
$countries = [];
43+
foreach ($data as $item) {
44+
$countries[$item->id] = new Location($item);
45+
}
46+
$this->cache($cache_key, $countries);
47+
return $countries;
48+
}
49+
50+
/**
51+
* Get country location objects for all countries.
52+
*
53+
* @return \Drupal\ghi_base_objects\ApiObjects\Location|null
54+
* A location object or NULL.
55+
*/
56+
public function getCountry($country_id) {
57+
$countries = $this->getCountries();
58+
return array_key_exists($country_id, $countries) ? $countries[$country_id] : NULL;
59+
}
60+
61+
}

0 commit comments

Comments
 (0)