diff --git a/.env.example b/.env.example
index 192b898f..c816726e 100644
--- a/.env.example
+++ b/.env.example
@@ -23,7 +23,7 @@ SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
-REDIS_HOST=127.0.0.1
+REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
@@ -55,3 +55,5 @@ VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
+
+CIPHERSWEET_KEY=b130a919cba9b7745d4b0144533308f2ffa38476cf562175aab8ca9f0e9648f1
diff --git a/README.md b/README.md
index 5c8b8e14..338f8723 100644
--- a/README.md
+++ b/README.md
@@ -20,21 +20,21 @@ docker run --rm \
```vendor/bin/sail up -d```
+### Migrate and seed the database
+
+```./vendor/bin/sail artisan migrate:fresh --seed```
+
#### Install node dependencies
```vendor/bin/sail yarn install```
-#### Start compiling watcher
+#### Start just in time compiler
```vendor/bin/sail yarn dev```
-#### Compile assets
-
-```vendor/bin/sail yarn build```
-
#### Update dependencies
-```vendor/bin/sail yarn install```
+```vendor/bin/sail yarn```
## Contributing
diff --git a/app/Http/Livewire/Meetup/PrepareForBtcMapItem.php b/app/Http/Livewire/Meetup/PrepareForBtcMapItem.php
index deb25142..9b9e946b 100644
--- a/app/Http/Livewire/Meetup/PrepareForBtcMapItem.php
+++ b/app/Http/Livewire/Meetup/PrepareForBtcMapItem.php
@@ -3,6 +3,7 @@
namespace App\Http\Livewire\Meetup;
use App\Models\Meetup;
+use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Livewire\Component;
@@ -14,138 +15,322 @@ class PrepareForBtcMapItem extends Component
public Meetup $meetup;
+ public $model;
+
+ public string $search = '';
+
public $population;
- public $population_date;
- public $osmSearchResults;
- public $osmSearchResultsState;
- public $osmSearchResultsCountry;
+ public $population_date = '';
- public $selectedItem;
+ public ?int $osm_id = null;
- public function rules()
+ public array $osmSearchResults = [];
+
+ public $selectedItemOSMPolygons;
+
+ public $selectedItemOSMBoundaries;
+
+ public $selectedItemPolygonsOSMfr;
+
+ public $polygonsOSMfrX = 0.020000;
+
+ public $polygonsOSMfrY = 0.005000;
+
+ public $polygonsOSMfrZ = 0.005000;
+
+ public $currentPercentage = 100;
+
+ public bool $OSMBoundaries = false;
+
+ public bool $polygonsOSMfr = false;
+
+ protected $queryString = [
+ 'search' => ['except' => ''],
+ 'osm_id' => ['except' => null],
+ ];
+
+ public function rules(): array
{
return [
- 'population' => 'required',
+ 'search' => 'required|string',
+ 'currentPercentage' => 'required|numeric',
+
+ 'model.simplified_geojson' => 'nullable',
+
+ 'OSMBoundaries' => 'bool',
+ 'polygonsOSMfr' => 'bool',
+
+ 'population' => 'required|numeric',
'population_date' => 'required|string',
+
+ 'polygonsOSMfrX' => 'numeric|max:1',
+ 'polygonsOSMfrY' => 'numeric|max:1',
+ 'polygonsOSMfrZ' => 'numeric|max:1',
];
}
- public function mount()
+ public function mount(): void
{
- $response = Http::acceptJson()
- ->get(
- 'https://nominatim.openstreetmap.org/search?city='.$this->meetup->city->name.'&format=json&polygon_geojson=1'
- );
- $this->osmSearchResults = $response->json();
-
- $response = Http::acceptJson()
- ->get(
- 'https://nominatim.openstreetmap.org/search?state='.$this->meetup->city->name.'&format=json&polygon_geojson=1'
- );
- $this->osmSearchResultsState = $response->json();
-
- $response = Http::acceptJson()
- ->get(
- 'https://nominatim.openstreetmap.org/search?country='.$this->meetup->city->name.'&format=json&polygon_geojson=1'
- );
- $this->osmSearchResultsCountry = $response->json();
-
- if ($this->meetup->city->osm_relation) {
- $this->selectedItem = $this->meetup->city->osm_relation;
+ $this->model = $this->meetup->city;
+ $this->population = $this->model->population;
+ $this->population_date = $this->model->population_date;
+ $this->getSearchResults();
+ if ($this->osm_id) {
+ $this->selectedItemOSMPolygons = collect($this->osmSearchResults)
+ ->firstWhere('osm_id', $this->osm_id);
+ $this->executeMapshaper($this->currentPercentage);
}
- $this->population = $this->meetup->city->population;
- $this->population_date = $this->meetup->city->population_date;
+ }
+
+ private function getSearchResults(): void
+ {
+ $responses = Http::pool(fn(Pool $pool) => [
+ $pool->acceptJson()
+ ->get(
+ sprintf('https://nominatim.openstreetmap.org/search?q=%s&format=json&polygon_geojson=1&polygon_threshold=0.0003&email='.config('services.nominatim.email'),
+ $this->search)
+ ),
+ ]);
+
+ $this->osmSearchResults = collect($responses[0]->json())
+ ->filter(fn($item
+ ) => (
+ $item['geojson']['type'] === 'Polygon'
+ || $item['geojson']['type'] === 'MultiPolygon'
+ )
+ && $item['osm_id']
+ && count($item['geojson']['coordinates'], COUNT_RECURSIVE) < 100000
+ )
+ ->values()
+ ->toArray();
+ }
+
+ private function executeMapshaper($percentage = 100): void
+ {
+ try {
+ // put OSM geojson to storage
+ Storage::disk('geo')
+ ->put('geojson_'.$this->selectedItemOSMPolygons['osm_id'].'.json',
+ json_encode($this->selectedItemOSMPolygons['geojson'], JSON_THROW_ON_ERROR)
+ );
+
+ // execute mapshaper
+ $input = storage_path('app/geo/geojson_'.$this->selectedItemOSMPolygons['osm_id'].'.json');
+ $output = storage_path('app/geo/output_'.$this->selectedItemOSMPolygons['osm_id'].'.json');
+ $mapShaperBinary = base_path('node_modules/mapshaper/bin/mapshaper');
+ exec($mapShaperBinary.' '.$input.' -simplify dp '.$percentage.'% -o '.$output);
+ $this->currentPercentage = $percentage;
+
+ $mapShaperOutput = str(
+ Storage::disk('geo')
+ ->get('output_'.$this->selectedItemOSMPolygons['osm_id'].'.json')
+ );
+ if ($mapShaperOutput->contains(['Polygon', 'MultiPolygon'])) {
+ // trim geojson
+ Storage::disk('geo')
+ ->put(
+ 'trimmed_'.$this->selectedItemOSMPolygons['osm_id'].'.json',
+ $mapShaperOutput
+ ->after('{"type":"GeometryCollection", "geometries": [')
+ ->beforeLast(']}')
+ ->toString()
+ );
+ } else {
+ $this->notification()
+ ->warning('Warning',
+ sprintf('Geojson is not valid. After simplification, it contains no polygons. Instead it contains: %s',
+ $mapShaperOutput->after('{"type":')
+ ->before(',')));
+
+ return;
+ }
+
+ // put trimmed geojson to model
+ $this->model->simplified_geojson = json_decode(
+ trim(
+ Storage::disk('geo')
+ ->get('trimmed_'.$this->selectedItemOSMPolygons['osm_id'].'.json')
+ ),
+ false, 512, JSON_THROW_ON_ERROR
+ );
+
+ // emit event for AlpineJS
+ $this->emit('geoJsonUpdated');
+
+ } catch (\Exception $e) {
+ $this->notification()
+ ->error('Error', $e->getMessage());
+ }
+ }
+
+ public function saveSimplifiedGeoJson()
+ {
+ $this->model->osm_relation = $this->osm_id;
+ $this->model->save();
+
+ $this->notification()
+ ->success('Success', 'Simplified GeoJSON saved.');
+ }
+
+ public function submitPolygonsOSM()
+ {
+ $this->validate();
+ $postGenerate = Http::acceptJson()
+ ->asForm()
+ ->post(
+ 'https://polygons.openstreetmap.fr/?id='.$this->selectedItemOSMPolygons['osm_id'],
+ [
+ 'x' => $this->polygonsOSMfrX,
+ 'y' => $this->polygonsOSMfrY,
+ 'z' => $this->polygonsOSMfrZ,
+ 'generate' => 'Submit+Query',
+ ]
+ );
+ if ($postGenerate->ok()) {
+ $getUrl = sprintf(
+ 'https://polygons.openstreetmap.fr/get_geojson.py?id=%s¶ms=%s-%s-%s',
+ $this->selectedItemOSMPolygons['osm_id'],
+ (float) str($this->polygonsOSMfrX)
+ ->before('.')
+ ->toString().'.'.str($this->polygonsOSMfrX)
+ ->after('.')
+ ->padRight(6, '0')
+ ->toString(),
+ (float) str($this->polygonsOSMfrY)
+ ->before('.')
+ ->toString().'.'.str($this->polygonsOSMfrY)
+ ->after('.')
+ ->padRight(6, '0')
+ ->toString(),
+ (float) str($this->polygonsOSMfrZ)
+ ->before('.')
+ ->toString().'.'.str($this->polygonsOSMfrZ)
+ ->after('.')
+ ->padRight(6, '0')
+ ->toString(),
+ );
+ $response = Http::acceptJson()
+ ->get($getUrl);
+ if ($response->json()) {
+ $this->selectedItemPolygonsOSMfr = $response->json();
+ $this->emit('geoJsonUpdated');
+ } else {
+ $this->notification()
+ ->warning('No data', 'No data found for this area.');
+ }
+ } else {
+ $this->notification()
+ ->error('Error', 'Something went wrong: '.$postGenerate->status());
+ }
+ }
+
+ public function submit(): void
+ {
+ $this->validate();
+ $this->getSearchResults();
+ }
+
+ public function selectItem($index): void
+ {
+ $this->OSMBoundaries = false;
+ $this->selectedItemOSMBoundaries = null;
+ $this->selectedItemOSMPolygons = $this->osmSearchResults[$index];
+ $this->osm_id = $this->selectedItemOSMPolygons['osm_id'];
+ $this->model->osm_relation = $this->selectedItemOSMPolygons;
+
+ $this->executeMapshaper(100);
+ }
+
+ public function updatedOSMBoundaries($value)
+ {
+ if ($value) {
+ $response = Http::acceptJson()
+ ->asForm()
+ ->post('https://osm-boundaries.com/Ajax/GetBoundary', [
+ 'db' => 'osm20221205',
+ 'waterOrLand' => 'water',
+ 'osmId' => '-'.$this->selectedItemOSMPolygons['osm_id'],
+ ]);
+ if ($response->json()) {
+ if (count($response->json()['coordinates'], COUNT_RECURSIVE) > 100000) {
+ $this->notification()
+ ->warning('Warning', 'Water boundaries are too big');
+
+ return;
+ }
+
+ $this->selectedItemOSMBoundaries = $response->json();
+ $this->emit('geoJsonUpdated');
+ } else {
+ $this->notification()
+ ->warning('Warning', 'No water boundaries found');
+ }
+ } else {
+ $this->selectedItemOSMBoundaries = null;
+ $this->emit('geoJsonUpdated');
+ }
+ }
+
+ public function updatedCurrentPercentage($value)
+ {
+ $this->executeMapshaper((float) $value);
+ }
+
+ public function setPercentage($percent): void
+ {
+ $this->executeMapshaper($percent);
}
public function updatedPopulation($value)
{
- $value = str($value)
- ->replace('.', '')
- ->replace(',', '.')
- ->toInteger();
- $this->meetup->city->population = $value;
- $this->population = $value;
- $this->meetup->city->save();
+ $this->model->population = (int) str($value)
+ ->replace(['.', ','], '')
+ ->toString();
+ $this->model->save();
+
$this->notification()
- ->success('Population updated', 'Success');
+ ->success('Success', 'Population saved.');
}
public function updatedPopulationDate($value)
{
- $this->meetup->city->population_date = $value;
- $this->meetup->city->save();
+ $this->model->population_date = $value;
+ $this->model->save();
+
$this->notification()
- ->success('Population date updated', 'Success');
- }
-
- public function selectItem($index, bool $isState = false, $isCountry = false)
- {
- if ($isState) {
- $this->selectedItem = $this->osmSearchResultsState[$index];
- } elseif ($isCountry) {
- $this->selectedItem = $this->osmSearchResultsCountry[$index];
- } else {
- $this->selectedItem = $this->osmSearchResults[$index];
- }
- Storage::disk('geo')
- ->put('geojson_'.$this->selectedItem['osm_id'].'.json',
- json_encode($this->selectedItem['geojson'], JSON_THROW_ON_ERROR));
- $input = storage_path('app/geo/geojson_'.$this->selectedItem['osm_id'].'.json');
- $output = storage_path('app/geo/output_'.$this->selectedItem['osm_id'].'.json');
- exec('mapshaper '.$input.' -simplify dp 4% -o '.$output);
- Storage::disk('geo')
- ->put(
- 'trimmed_'.$this->selectedItem['osm_id'].'.json',
- str(Storage::disk('geo')
- ->get('output_'.$this->selectedItem['osm_id'].'.json'))
- ->after('{"type":"GeometryCollection", "geometries": [')
- ->beforeLast(']}')
- ->toString()
- );
- $this->meetup->city->osm_relation = $this->selectedItem;
- $this->meetup->city->simplified_geojson = json_decode(trim(Storage::disk('geo')
- ->get('trimmed_'.$this->selectedItem['osm_id'].'.json')),
- false, 512, JSON_THROW_ON_ERROR);
- $this->meetup->city->population = 0;
- $this->meetup->city->population_date = '2021-12-31';
- $this->meetup->city->save();
-
- return to_route('osm.meetups.item', ['meetup' => $this->meetup]);
- }
-
- public function setPercent($percent)
- {
- $input = storage_path('app/geo/geojson_'.$this->selectedItem['osm_id'].'.json');
- $output = storage_path('app/geo/output_'.$this->selectedItem['osm_id'].'.json');
- exec('mapshaper '.$input.' -simplify dp '.$percent.'% -o '.$output);
- Storage::disk('geo')
- ->put(
- 'trimmed_'.$this->selectedItem['osm_id'].'.json',
- str(Storage::disk('geo')
- ->get('output_'.$this->selectedItem['osm_id'].'.json'))
- ->after('{"type":"GeometryCollection", "geometries": [')
- ->beforeLast(']}')
- ->toString()
- );
- $this->meetup->city->simplified_geojson = json_decode(trim(Storage::disk('geo')
- ->get('trimmed_'.$this->selectedItem['osm_id'].'.json')),
- false, 512, JSON_THROW_ON_ERROR);
- $this->meetup->city->save();
-
- return to_route('osm.meetups.item', ['meetup' => $this->meetup]);
- }
-
- public function takePop($value)
- {
- $this->meetup->city->population = $value;
- $this->meetup->city->population_date = '2021-12-31';
- $this->meetup->city->save();
-
- return to_route('osm.meetups.item', ['meetup' => $this->meetup]);
+ ->success('Success', 'Population saved.');
}
public function render()
{
- return view('livewire.meetup.prepare-for-btc-map-item');
+ return view('livewire.meetup.prepare-for-btc-map-item', [
+ 'percentages' => collect([
+ 0.5,
+ 0.75,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 15,
+ 20,
+ 25,
+ 30,
+ 40,
+ 50,
+ 60,
+ 75,
+ 80,
+ 100,
+ ])
+ ->reverse()
+ ->values()
+ ->toArray(),
+ ]);
}
}
diff --git a/app/Http/Livewire/Tables/MeetupForBtcMapTable.php b/app/Http/Livewire/Tables/MeetupForBtcMapTable.php
index 3e24ae2c..2ad6c36a 100644
--- a/app/Http/Livewire/Tables/MeetupForBtcMapTable.php
+++ b/app/Http/Livewire/Tables/MeetupForBtcMapTable.php
@@ -18,6 +18,7 @@ class MeetupForBtcMapTable extends DataTableComponent
'simplified_geojson',
'population',
'population_date',
+ 'city_id',
])
->setPerPageAccepted([
100000,
diff --git a/app/Models/Meetup.php b/app/Models/Meetup.php
index 5f5dd6a4..3a3ce481 100644
--- a/app/Models/Meetup.php
+++ b/app/Models/Meetup.php
@@ -30,9 +30,10 @@ class Meetup extends Model implements HasMedia
* @var array
*/
protected $casts = [
- 'id' => 'integer',
- 'city_id' => 'integer',
- 'github_data' => 'json',
+ 'id' => 'integer',
+ 'city_id' => 'integer',
+ 'github_data' => 'json',
+ 'simplified_geojson' => 'array',
];
protected static function booted()
diff --git a/resources/views/columns/meetups/osm-actions.blade.php b/resources/views/columns/meetups/osm-actions.blade.php
index 7ba6561a..32b92c5b 100644
--- a/resources/views/columns/meetups/osm-actions.blade.php
+++ b/resources/views/columns/meetups/osm-actions.blade.php
@@ -16,7 +16,7 @@
-
- {{ $item['display_name'] }}
-
-
- @endforeach
-
-
- {{ $item['display_name'] }}
-
-
- @endforeach
-
-
- {{ $item['display_name'] }}
-
-
- @endforeach
- {{ json_encode($meetup->city->simplified_geojson, JSON_THROW_ON_ERROR) }}
- + {{ $item['display_name'] }}
+
+
{{ $jsonEncodedSelectedItem }}
+ {{ $jsonEncodedSimplifiedGeoJson }}
+ {{ $jsonEncodedGeoJsonWater }}
+ {{ $jsonEncodedGeoJsonOSMFr }}
+ + GeoJSON helper is maintained by HolgerHatGarKeineNode [npub1pt0kw36ue3w2g4haxq3wgm6a2fhtptmzsjlc2j2vphtcgle72qesgpjyc6]. + This + software is open-sourced software + licensed under the MIT license. +
+
- osm_id: {{ $meetup->city->osm_relation['osm_id'] }}
-
-
- display_name: {{ $meetup->city->osm_relation['display_name'] }}
-
- @endif
-
- population: {{ $meetup->city->population }}
-
-
- population date: {{ $meetup->city->population_date }}
-