* initial commit * add lists * update permissions * fix waypoint create * needs_full_sync for activitypub trails * fixes build issues * fix list get * further CSRF protection * update API docs * adds rate limiter * fix tiptap mentions * a bit more cleanup of main.go * Fix migration order * fixes reviewed notes * require context for activitpub server calls * improve hashing for identifier * fix copy paste error * fix sync trail/list issues * fix summit log/comment duplicates * adaptions after trail merge * fix dockerignore --------- Co-authored-by: Christian Beutel <> Co-authored-by: slothful-vassal <89943360+slothful-vassal@users.noreply.github.com>
198 lines
5.0 KiB
Go
198 lines
5.0 KiB
Go
package routes
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/pocketbase/pocketbase/apis"
|
|
"github.com/pocketbase/pocketbase/core"
|
|
|
|
"pocketbase/util"
|
|
)
|
|
|
|
const defaultWaypointMergeRadius = 50
|
|
|
|
type waypointMergeSettings struct {
|
|
Enabled bool
|
|
Radius float64
|
|
}
|
|
|
|
type waypointClusterRequest struct {
|
|
Category string `json:"category"`
|
|
Photos []waypointClusterPhoto `json:"photos"`
|
|
Waypoints []waypointClusterWaypoint `json:"waypoints"`
|
|
}
|
|
|
|
type waypointClusterPhoto struct {
|
|
ID string `json:"id"`
|
|
Lat float64 `json:"lat"`
|
|
Lon float64 `json:"lon"`
|
|
}
|
|
|
|
type waypointClusterWaypoint struct {
|
|
ID string `json:"id"`
|
|
Lat float64 `json:"lat"`
|
|
Lon float64 `json:"lon"`
|
|
}
|
|
|
|
type waypointPhotoCluster struct {
|
|
Waypoint string `json:"waypoint,omitempty"`
|
|
Photos []string `json:"photos"`
|
|
SumLat float64 `json:"-"`
|
|
SumLon float64 `json:"-"`
|
|
Count int `json:"-"`
|
|
Lat float64 `json:"lat"`
|
|
Lon float64 `json:"lon"`
|
|
}
|
|
|
|
type categorySettings struct {
|
|
WaypointMergeEnabled *bool `json:"wp_merge_enabled"`
|
|
WaypointMergeRadius *float64 `json:"wp_merge_radius"`
|
|
}
|
|
|
|
func WaypointCluster(e *core.RequestEvent) error {
|
|
if e.Auth == nil {
|
|
return apis.NewUnauthorizedError("authentication required", nil)
|
|
}
|
|
|
|
var data waypointClusterRequest
|
|
if err := e.BindBody(&data); err != nil {
|
|
return apis.NewBadRequestError("Failed to read request data", err)
|
|
}
|
|
|
|
if data.Category != "" && len(data.Category) != 15 {
|
|
return apis.NewBadRequestError("Invalid category", nil)
|
|
}
|
|
|
|
for _, photo := range data.Photos {
|
|
if photo.ID == "" {
|
|
return apis.NewBadRequestError("Invalid photo id", nil)
|
|
}
|
|
if photo.Lat < -90 || photo.Lat > 90 {
|
|
return apis.NewBadRequestError("Invalid photo latitude", nil)
|
|
}
|
|
if photo.Lon < -180 || photo.Lon > 180 {
|
|
return apis.NewBadRequestError("Invalid photo longitude", nil)
|
|
}
|
|
}
|
|
|
|
for _, waypoint := range data.Waypoints {
|
|
if waypoint.ID == "" {
|
|
return apis.NewBadRequestError("Invalid waypoint id", nil)
|
|
}
|
|
if waypoint.Lat < -90 || waypoint.Lat > 90 {
|
|
return apis.NewBadRequestError("Invalid waypoint latitude", nil)
|
|
}
|
|
if waypoint.Lon < -180 || waypoint.Lon > 180 {
|
|
return apis.NewBadRequestError("Invalid waypoint longitude", nil)
|
|
}
|
|
}
|
|
|
|
mergeSettings, err := getWaypointMergeSettings(e.App, data.Category)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return e.JSON(http.StatusOK, map[string]any{
|
|
"mergeEnabled": mergeSettings.Enabled,
|
|
"mergeRadius": mergeSettings.Radius,
|
|
"clusters": clusterWaypointPhotos(data.Photos, data.Waypoints, mergeSettings),
|
|
})
|
|
}
|
|
|
|
func getWaypointMergeSettings(app core.App, categoryId string) (waypointMergeSettings, error) {
|
|
defaultSettings := waypointMergeSettings{
|
|
Enabled: true,
|
|
Radius: defaultWaypointMergeRadius,
|
|
}
|
|
|
|
if categoryId == "" {
|
|
return defaultSettings, nil
|
|
}
|
|
|
|
category, err := app.FindRecordById("categories", categoryId)
|
|
if err != nil {
|
|
return waypointMergeSettings{}, err
|
|
}
|
|
|
|
var settings categorySettings
|
|
if err := category.UnmarshalJSONField("settings", &settings); err != nil {
|
|
return defaultSettings, nil
|
|
}
|
|
|
|
if settings.WaypointMergeEnabled != nil {
|
|
defaultSettings.Enabled = *settings.WaypointMergeEnabled
|
|
}
|
|
|
|
if settings.WaypointMergeRadius != nil && *settings.WaypointMergeRadius >= 0 {
|
|
defaultSettings.Radius = *settings.WaypointMergeRadius
|
|
}
|
|
|
|
return defaultSettings, nil
|
|
}
|
|
|
|
func clusterWaypointPhotos(photos []waypointClusterPhoto, waypoints []waypointClusterWaypoint, mergeSettings waypointMergeSettings) []waypointPhotoCluster {
|
|
clusters := []waypointPhotoCluster{}
|
|
|
|
if mergeSettings.Enabled {
|
|
for _, waypoint := range waypoints {
|
|
clusters = append(clusters, newWaypointCluster(waypoint))
|
|
}
|
|
}
|
|
|
|
for _, photo := range photos {
|
|
if !mergeSettings.Enabled {
|
|
clusters = append(clusters, newWaypointPhotoCluster(photo))
|
|
continue
|
|
}
|
|
|
|
matchingClusterIndex := -1
|
|
for i, cluster := range clusters {
|
|
distanceToCenter := util.HaversineDistance(cluster.Lat, cluster.Lon, photo.Lat, photo.Lon)
|
|
if distanceToCenter <= mergeSettings.Radius {
|
|
matchingClusterIndex = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if matchingClusterIndex >= 0 {
|
|
addPhotoToWaypointCluster(&clusters[matchingClusterIndex], photo)
|
|
} else {
|
|
clusters = append(clusters, newWaypointPhotoCluster(photo))
|
|
}
|
|
}
|
|
|
|
return clusters
|
|
}
|
|
|
|
func newWaypointPhotoCluster(photo waypointClusterPhoto) waypointPhotoCluster {
|
|
return waypointPhotoCluster{
|
|
Photos: []string{photo.ID},
|
|
SumLat: photo.Lat,
|
|
SumLon: photo.Lon,
|
|
Count: 1,
|
|
Lat: photo.Lat,
|
|
Lon: photo.Lon,
|
|
}
|
|
}
|
|
|
|
func newWaypointCluster(waypoint waypointClusterWaypoint) waypointPhotoCluster {
|
|
return waypointPhotoCluster{
|
|
Waypoint: waypoint.ID,
|
|
Photos: []string{},
|
|
SumLat: waypoint.Lat,
|
|
SumLon: waypoint.Lon,
|
|
Count: 1,
|
|
Lat: waypoint.Lat,
|
|
Lon: waypoint.Lon,
|
|
}
|
|
}
|
|
|
|
func addPhotoToWaypointCluster(cluster *waypointPhotoCluster, photo waypointClusterPhoto) {
|
|
cluster.Photos = append(cluster.Photos, photo.ID)
|
|
cluster.SumLat += photo.Lat
|
|
cluster.SumLon += photo.Lon
|
|
cluster.Count++
|
|
cluster.Lat = cluster.SumLat / float64(cluster.Count)
|
|
cluster.Lon = cluster.SumLon / float64(cluster.Count)
|
|
}
|