Skip to content

Commit

Permalink
feat: added geoJSON upload
Browse files Browse the repository at this point in the history
  • Loading branch information
mwargan committed Feb 27, 2024
1 parent 74ef571 commit 8d3e6b1
Show file tree
Hide file tree
Showing 6 changed files with 2,618 additions and 14 deletions.
5 changes: 4 additions & 1 deletion app/Http/Controllers/MarkerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Models\Map;
use App\Models\Marker;
use App\Models\MarkerLocation;
use App\Parsers\Files\GeoJSONParser;
use App\Parsers\Files\GPXParser;
use Carbon\Carbon;
use MatanYadaev\EloquentSpatial\Objects\Point;
Expand Down Expand Up @@ -324,7 +325,7 @@ public function storeInBulkFromFile(Request $request, Map $map)
$request->validate([
'file' => [
'required',
File::types(['gpx'])
File::types(['gpx', 'geojson'])
->max(1024 * 3)
],
]);
Expand All @@ -334,6 +335,8 @@ public function storeInBulkFromFile(Request $request, Map $map)

if (Str::contains($fileMimeType, 'gpx')) {
$parser = new GPXParser();
} elseif (Str::contains($fileMimeType, 'json')) {
$parser = new GeoJSONParser();
} else {
return response()->json(['error' => 'File type not supported'], 422);
}
Expand Down
8 changes: 8 additions & 0 deletions app/Parsers/Files/FIleParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,12 @@ abstract public function parseFile(string $filepath): array;
* @return array{category_name?: string, description?: string, link?: string, created_at?: string, updated_at?: string, meta: array, lat?: float, lng?: float, elevation?: float, locations?: array}
*/
abstract public function parseMarkersFromFile(string $filepath): array;

/**
* Parse the map details from the file.
*
* @param string $filepath
* @return array{title?: string, description?: string}
*/
abstract public function parseMapDetailsFromFile(string $filepath): array;
}
17 changes: 4 additions & 13 deletions app/Parsers/Files/GPXParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@

class GPXParser extends FIleParser
{
/**
* Parse the GPX file.
*
* @param string $filepath
* @return array
*/
public function parseFile(string $filepath): array
{
return [
Expand Down Expand Up @@ -135,12 +129,6 @@ public function parseMarkersFromFile(string $filepath): array
return $markers;
}

/**
* Parse the map details, like the name, description, author, etc.
*
* @param string $filepath
* @return array
*/
public function parseMapDetailsFromFile(string $filepath): array
{
$xml = simplexml_load_file($filepath);
Expand All @@ -153,6 +141,9 @@ public function parseMapDetailsFromFile(string $filepath): array
$mapDetails = $array['metadata'];
}

return $mapDetails;
return [
'title' => $mapDetails['name'] ?? null,
'description' => $mapDetails['desc'] ?? null,
];
}
}
79 changes: 79 additions & 0 deletions app/Parsers/Files/GeoJSONParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace App\Parsers\Files;

class GeoJSONParser extends FIleParser
{
public function parseFile(string $filepath): array
{
return [
'map' => $this->parseMapDetailsFromFile($filepath),
'markers' => $this->parseMarkersFromFile($filepath),
];
}

public function parseMarkersFromFile(string $filepath): array
{
$json = file_get_contents($filepath);
$data = json_decode($json, true);

$markers = [];

if (isset($data['features'])) {
foreach ($data['features'] as $feature) {
// Depending on the type of geometry, the coordinates can be in different places
// If its just a point, the coordinates are in the first level of the geometry object
if ($feature['geometry']['type'] === 'Point') {
$markers[] = [
'lat' => $feature['geometry']['coordinates'][1],
'lng' => $feature['geometry']['coordinates'][0],
'category_name' => $feature['properties']['name'] ?? 'Feature',
'description' => $feature['properties']['description'] ?? null,
'link' => $feature['properties']['link'] ?? null,
'elevation' => $feature['properties']['elevation'] ?? null,
'created_at' => $feature['properties']['time'] ?? null,
'updated_at' => $feature['properties']['time'] ?? null,
'meta' => $feature['properties'],
];
} elseif ($feature['geometry']['type'] === 'LineString') {
// Else if its a lineString, we have to add it to markers.locations for each coordinate

$locations = [];

$pointers = 0;
foreach ($feature['geometry']['coordinates'] as $coordinate) {
$locations[] = [
'lat' => $coordinate[1],
'lng' => $coordinate[0],
'elevation' => $coordinate[2] ?? null,

// There may be a coordTimes array in the properties, which we can use to set the created_at and updated_at
'created_at' => $feature['properties']['coordTimes'][$pointers] ?? null,
'updated_at' => $feature['properties']['coordTimes'][$pointers] ?? null,
];
}

$markers[] = [
'category_name' => $feature['properties']['name'] ?? 'Feature',
'description' => $feature['properties']['description'] ?? null,
'link' => $feature['properties']['link'] ?? null,
'created_at' => $feature['properties']['time'] ?? null,
'updated_at' => $feature['properties']['time'] ?? null,
'meta' => $feature['properties'],
'locations' => $locations,
];
}
}
}

return $markers;
}

public function parseMapDetailsFromFile(string $filepath): array
{
return [
'name' => pathinfo($filepath, PATHINFO_FILENAME),
'description' => 'A map of ' . pathinfo($filepath, PATHINFO_FILENAME),
];
}
}
51 changes: 51 additions & 0 deletions tests/Unit/MarkerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,57 @@ public function testCreateMarkerInBulkWithGpxFile()
$this->assertEquals(11, $map->markers()->whereNotNull('meta->number')->count());
}

public function testCreateMarkerInBulkWithGeoJSONFile()
{
$this->withoutExceptionHandling();

// Skip any dispatched jobs
$this->expectsJobs([
FillMissingMarkerElevation::class,
FillMissingLocationGeocodes::class,
]);

// We need to clean up the database before we start
DB::table('markers')->delete();
DB::table('marker_locations')->delete();

$map = new \App\Models\Map();
$map->users_can_create_markers = 'yes';
$map->options = ['links' => 'optional'];
$map->save();

$user = User::factory()->create();

/**
* @var \Illuminate\Contracts\Auth\Authenticatable
*/
$user = $user->givePermissionTo('create markers in bulk');

$this->actingAs($user, 'api');

$geojson = file_get_contents(base_path('tests/fixtures/ashland.geojson'));

$file = UploadedFile::fake()->createWithContent('ashland.geojson', $geojson);

$response = $this->postJson('/api/maps/' . $map->uuid . '/markers/file', ['file' => $file]);
$response->assertStatus(200);

// The ashland DB file shoulda add 11 </trk>, 25 wpt, so we should have 36 markers
$this->assertEquals(36, $map->markers()->count());

// The DB file adds 348 trkpt, so we should have 348 locations + 25 wpt
$this->assertEquals(373, $map->markerLocations()->count());

// 271 of the locations should have an elevation - note its normal this one is lower than the GPX one, some of the elevations were lost in the conversion to GeoJSON
$this->assertEquals(265, $map->markerLocations()->whereNotNull('elevation')->count());

// 267 should have created_at and updated_at on 2002-04-21 (any time)
$this->assertEquals(267, $map->markerLocations()->whereDate('marker_locations.created_at', '2002-04-21')->whereDate('marker_locations.updated_at', '2002-04-21')->count());

// The others should have todays date
$this->assertEquals(106, $map->markerLocations()->whereDate('marker_locations.created_at', now()->toDateString())->whereDate('marker_locations.updated_at', now()->toDateString())->count());
}

/**
* A basic test example.
*
Expand Down
Loading

0 comments on commit 8d3e6b1

Please sign in to comment.