⚡ if an event has geo coordinates, add links to routing on OSM, with correct place and zoom of 14, 3 buttons to get routig as car, bike, and by feet.
Signed-off-by: Baptiste Lemoine <contact@cipherbliss.com>
This commit is contained in:
parent
c8fb5bb80e
commit
5c57f1ce3c
@ -210,6 +210,9 @@ config :mobilizon, :maps,
|
||||
tiles: [
|
||||
endpoint: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
attribution: "© The OpenStreetMap Contributors"
|
||||
],
|
||||
routing: [
|
||||
type: :openstreetmap
|
||||
]
|
||||
|
||||
config :mobilizon, :anonymous,
|
||||
|
@ -238,6 +238,12 @@ type Tag {
|
||||
related: [Tag]
|
||||
}
|
||||
|
||||
"Instance map routing configuration"
|
||||
type Routing {
|
||||
"The instance's routing type"
|
||||
type: RoutingType
|
||||
}
|
||||
|
||||
"Language information"
|
||||
type Language {
|
||||
"The iso-639-3 language code"
|
||||
@ -420,6 +426,9 @@ type Events {
|
||||
type Maps {
|
||||
"The instance's maps tiles configuration"
|
||||
tiles: Tiles
|
||||
|
||||
"The instance's maps routing configuration"
|
||||
routing: Routing
|
||||
}
|
||||
|
||||
"Search groups result"
|
||||
@ -659,6 +668,14 @@ interface Interactable {
|
||||
url: String
|
||||
}
|
||||
|
||||
enum RoutingType {
|
||||
"Redirect to openstreetmap.org's direction endpoint"
|
||||
OPENSTREETMAP
|
||||
|
||||
"Redirect to Google Maps's direction endpoint"
|
||||
GOOGLE_MAPS
|
||||
}
|
||||
|
||||
"A struct containing the id of the deleted object"
|
||||
type DeletedObject {
|
||||
id: ID
|
||||
@ -2856,7 +2873,7 @@ type PaginatedGroupList {
|
||||
total: Int
|
||||
}
|
||||
|
||||
"Instance tiles configuration"
|
||||
"Instance map tiles configuration"
|
||||
type Tiles {
|
||||
"The instance's tiles endpoint"
|
||||
endpoint: String
|
||||
|
@ -51,6 +51,9 @@ export const CONFIG = gql`
|
||||
endpoint
|
||||
attribution
|
||||
}
|
||||
routing {
|
||||
type
|
||||
}
|
||||
}
|
||||
geocoding {
|
||||
provider
|
||||
|
@ -813,12 +813,15 @@
|
||||
"View all events": "View all events",
|
||||
"You will find here all the events you have created or of which you are a participant.": "You will find here all the events you have created or of which you are a participant.",
|
||||
"Create event": "Create event",
|
||||
"You didn't create or join any event yet": "You didn't create or join any event yet",
|
||||
"You didn't create or join any event yet.": "You didn't create or join any event yet.",
|
||||
"create an event": "create an event",
|
||||
"explore the events": "explore the events",
|
||||
"Do you wish to {create_event} or {explore_events}?": "Do you wish to {create_event} or {explore_events}?",
|
||||
"You are not part of any group": "You are not part of any group",
|
||||
"You are not part of any group.": "You are not part of any group.",
|
||||
"create a group": "create a group",
|
||||
"explore the groups": "explore the groups",
|
||||
"Do you wish to {create_group} or {explore_groups}?": "Do you wish to {create_group} or {explore_groups}?"
|
||||
"Do you wish to {create_group} or {explore_groups}?": "Do you wish to {create_group} or {explore_groups}?",
|
||||
"Type or select a date…": "Type or select a date…",
|
||||
"Getting there": "Getting there",
|
||||
"Groups are not enabled on this instance.": "Groups are not enabled on this instance."
|
||||
}
|
||||
|
@ -914,5 +914,8 @@
|
||||
"You are not part of any group.": "Vous ne faites partie d'aucun groupe.",
|
||||
"create a group": "créer un groupe",
|
||||
"explore the groups": "explorer les groupes",
|
||||
"Do you wish to {create_group} or {explore_groups}?": "Voulez-vous {create_group} ou {explore_groups} ?"
|
||||
"Do you wish to {create_group} or {explore_groups}?": "Voulez-vous {create_group} ou {explore_groups} ?",
|
||||
"Type or select a date…": "Entrez ou sélectionnez une date…",
|
||||
"Getting there": "S'y rendre",
|
||||
"Groups are not enabled on this instance.": "Les groupes ne sont pas activés sur cette instance."
|
||||
}
|
||||
|
@ -15,6 +15,12 @@ export interface IAddress {
|
||||
originId?: string;
|
||||
}
|
||||
|
||||
export interface IPoiInfo {
|
||||
name: string;
|
||||
alternativeName: string;
|
||||
poiIcon: IPOIIcon;
|
||||
}
|
||||
|
||||
export class Address implements IAddress {
|
||||
country = "";
|
||||
|
||||
@ -54,7 +60,7 @@ export class Address implements IAddress {
|
||||
this.originId = hash.originId;
|
||||
}
|
||||
|
||||
get poiInfos(): { name: string; alternativeName: string; poiIcon: IPOIIcon } {
|
||||
get poiInfos(): IPoiInfo {
|
||||
/* generate name corresponding to poi type */
|
||||
let name = "";
|
||||
let alternativeName = "";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { InstancePrivacyType, InstanceTermsType } from "./enums";
|
||||
import { InstancePrivacyType, InstanceTermsType, RoutingType } from "./enums";
|
||||
import type { IProvider } from "./resource";
|
||||
|
||||
export interface IOAuthProvider {
|
||||
@ -58,6 +58,9 @@ export interface IConfig {
|
||||
endpoint: string;
|
||||
attribution: string | null;
|
||||
};
|
||||
routing: {
|
||||
type: RoutingType;
|
||||
};
|
||||
};
|
||||
geocoding: {
|
||||
provider: string;
|
||||
|
@ -160,3 +160,15 @@ export enum Openness {
|
||||
MODERATED = "MODERATED",
|
||||
OPEN = "OPEN",
|
||||
}
|
||||
|
||||
export enum RoutingType {
|
||||
OPENSTREETMAP = "OPENSTREETMAP",
|
||||
GOOGLE_MAPS = "GOOGLE_MAPS",
|
||||
}
|
||||
|
||||
export enum RoutingTransportationType {
|
||||
FOOT = "FOOT",
|
||||
BIKE = "BIKE",
|
||||
TRANSIT = "TRANSIT",
|
||||
CAR = "CAR",
|
||||
}
|
||||
|
168
js/src/views/Event/Event.vue
Normal file → Executable file
168
js/src/views/Event/Event.vue
Normal file → Executable file
@ -84,9 +84,9 @@
|
||||
<tag>{{ tag.title }}</tag>
|
||||
</router-link>
|
||||
</p>
|
||||
<b-tag type="is-warning" size="is-medium" v-if="event.draft">{{
|
||||
$t("Draft")
|
||||
}}</b-tag>
|
||||
<b-tag type="is-warning" size="is-medium" v-if="event.draft"
|
||||
>{{ $t("Draft") }}
|
||||
</b-tag>
|
||||
<span
|
||||
class="event-status"
|
||||
v-if="event.status !== EventStatus.CONFIRMED"
|
||||
@ -325,7 +325,7 @@
|
||||
<span
|
||||
class="map-show-button"
|
||||
@click="showMap = !showMap"
|
||||
v-if="physicalAddress && physicalAddress.geom"
|
||||
v-if="physicalAddress.geom"
|
||||
>{{ $t("Show map") }}</span
|
||||
>
|
||||
</div>
|
||||
@ -526,8 +526,8 @@
|
||||
class="button"
|
||||
ref="cancelButton"
|
||||
@click="isJoinConfirmationModalActive = false"
|
||||
>{{ $t("Cancel") }}</b-button
|
||||
>
|
||||
>{{ $t("Cancel") }}
|
||||
</b-button>
|
||||
<b-button type="is-primary" native-type="submit">
|
||||
{{ $t("Confirm my participation") }}
|
||||
</b-button>
|
||||
@ -537,10 +537,18 @@
|
||||
</div>
|
||||
</b-modal>
|
||||
<b-modal
|
||||
class="map-modal"
|
||||
v-if="physicalAddress && physicalAddress.geom"
|
||||
:active.sync="showMap"
|
||||
has-modal-card
|
||||
full-screen
|
||||
>
|
||||
<div class="map">
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<button type="button" class="delete" @click="showMap = false" />
|
||||
</header>
|
||||
<div class="modal-card-body">
|
||||
<section class="map">
|
||||
<map-leaflet
|
||||
:coords="physicalAddress.geom"
|
||||
:marker="{
|
||||
@ -548,6 +556,58 @@
|
||||
icon: physicalAddress.poiInfos.poiIcon.icon,
|
||||
}"
|
||||
/>
|
||||
</section>
|
||||
<section class="columns is-centered map-footer">
|
||||
<div class="column is-half has-text-centered">
|
||||
<p class="address">
|
||||
<i class="mdi mdi-map-marker"></i>
|
||||
{{ physicalAddress.fullName }}
|
||||
</p>
|
||||
<p class="getting-there">{{ $t("Getting there") }}</p>
|
||||
<div
|
||||
class="buttons"
|
||||
v-if="
|
||||
addressLinkToRouteByCar ||
|
||||
addressLinkToRouteByBike ||
|
||||
addressLinkToRouteByFeet
|
||||
"
|
||||
>
|
||||
<a
|
||||
class="button"
|
||||
target="_blank"
|
||||
v-if="addressLinkToRouteByFeet"
|
||||
:href="addressLinkToRouteByFeet"
|
||||
>
|
||||
<i class="mdi mdi-walk"></i
|
||||
></a>
|
||||
<a
|
||||
class="button"
|
||||
target="_blank"
|
||||
v-if="addressLinkToRouteByBike"
|
||||
:href="addressLinkToRouteByBike"
|
||||
>
|
||||
<i class="mdi mdi-bike"></i
|
||||
></a>
|
||||
<a
|
||||
class="button"
|
||||
target="_blank"
|
||||
v-if="addressLinkToRouteByTransit"
|
||||
:href="addressLinkToRouteByTransit"
|
||||
>
|
||||
<i class="mdi mdi-bus"></i
|
||||
></a>
|
||||
<a
|
||||
class="button"
|
||||
target="_blank"
|
||||
v-if="addressLinkToRouteByCar"
|
||||
:href="addressLinkToRouteByCar"
|
||||
>
|
||||
<i class="mdi mdi-car"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</div>
|
||||
@ -563,6 +623,8 @@ import {
|
||||
EventStatus,
|
||||
EventVisibility,
|
||||
ParticipantRole,
|
||||
RoutingTransportationType,
|
||||
RoutingType,
|
||||
} from "@/types/enums";
|
||||
import {
|
||||
EVENT_PERSON_PARTICIPATION,
|
||||
@ -602,6 +664,7 @@ import ActorCard from "../../components/Account/ActorCard.vue";
|
||||
import PopoverActorCard from "../../components/Account/PopoverActorCard.vue";
|
||||
import { IParticipant } from "../../types/participant.model";
|
||||
|
||||
// noinspection TypeScriptValidateTypes
|
||||
@Component({
|
||||
components: {
|
||||
EventMetadataBlock,
|
||||
@ -734,6 +797,65 @@ export default class Event extends EventMixin {
|
||||
|
||||
messageForConfirmation = "";
|
||||
|
||||
RoutingParamType = {
|
||||
[RoutingType.OPENSTREETMAP]: {
|
||||
[RoutingTransportationType.FOOT]: "engine=fossgis_osrm_foot",
|
||||
[RoutingTransportationType.BIKE]: "engine=fossgis_osrm_bike",
|
||||
[RoutingTransportationType.TRANSIT]: null,
|
||||
[RoutingTransportationType.CAR]: "engine=fossgis_osrm_car",
|
||||
},
|
||||
[RoutingType.GOOGLE_MAPS]: {
|
||||
[RoutingTransportationType.FOOT]: "dirflg=w",
|
||||
[RoutingTransportationType.BIKE]: "dirflg=b",
|
||||
[RoutingTransportationType.TRANSIT]: "dirflg=r",
|
||||
[RoutingTransportationType.CAR]: "driving",
|
||||
},
|
||||
};
|
||||
|
||||
makeNavigationPath(
|
||||
transportationType: RoutingTransportationType
|
||||
): string | undefined {
|
||||
const geometry = this.physicalAddress?.geom;
|
||||
if (geometry) {
|
||||
const routingType = this.config.maps.routing.type;
|
||||
/**
|
||||
* build urls to routing map
|
||||
*/
|
||||
if (!this.RoutingParamType[routingType][transportationType]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const urlGeometry = geometry.split(";").reverse().join(",");
|
||||
|
||||
switch (routingType) {
|
||||
case RoutingType.GOOGLE_MAPS:
|
||||
return `https://maps.google.com/?saddr=Current+Location&daddr=${urlGeometry}&${this.RoutingParamType[routingType][transportationType]}`;
|
||||
case RoutingType.OPENSTREETMAP:
|
||||
default: {
|
||||
const bboxX = geometry.split(";").reverse()[0];
|
||||
const bboxY = geometry.split(";").reverse()[1];
|
||||
return `https://www.openstreetmap.org/directions?from=&to=${urlGeometry}&${this.RoutingParamType[routingType][transportationType]}#map=14/${bboxX}/${bboxY}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get addressLinkToRouteByCar(): undefined | string {
|
||||
return this.makeNavigationPath(RoutingTransportationType.CAR);
|
||||
}
|
||||
|
||||
get addressLinkToRouteByBike(): undefined | string {
|
||||
return this.makeNavigationPath(RoutingTransportationType.BIKE);
|
||||
}
|
||||
|
||||
get addressLinkToRouteByFeet(): undefined | string {
|
||||
return this.makeNavigationPath(RoutingTransportationType.FOOT);
|
||||
}
|
||||
|
||||
get addressLinkToRouteByTransit(): undefined | string {
|
||||
return this.makeNavigationPath(RoutingTransportationType.TRANSIT);
|
||||
}
|
||||
|
||||
get eventTitle(): undefined | string {
|
||||
if (!this.event) return undefined;
|
||||
return this.event.title;
|
||||
@ -1096,6 +1218,7 @@ export default class Event extends EventMixin {
|
||||
|
||||
get physicalAddress(): Address | null {
|
||||
if (!this.event.physicalAddress) return null;
|
||||
|
||||
return new Address(this.event.physicalAddress);
|
||||
}
|
||||
|
||||
@ -1225,6 +1348,7 @@ div.sidebar {
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
span {
|
||||
&.tag {
|
||||
margin: 0 2px;
|
||||
@ -1389,9 +1513,31 @@ a.participations-link {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
div.map {
|
||||
height: 900px;
|
||||
width: 100%;
|
||||
padding: 25px 5px 0;
|
||||
.map-modal {
|
||||
.modal-card-head {
|
||||
justify-content: flex-end;
|
||||
button.delete {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
section.map {
|
||||
height: calc(100% - 8rem);
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
section.map-footer {
|
||||
p.address {
|
||||
margin: 1rem auto;
|
||||
}
|
||||
div.buttons {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-border {
|
||||
border: 0;
|
||||
cursor: auto;
|
||||
}
|
||||
</style>
|
||||
|
@ -127,7 +127,7 @@
|
||||
</span>
|
||||
</template>
|
||||
<b-message v-if="config && !config.features.groups" type="is-danger">
|
||||
{{ $t("Groups are not enabled on your server.") }}
|
||||
{{ $t("Groups are not enabled on this instance.") }}
|
||||
</b-message>
|
||||
<div v-else-if="searchGroups.total > 0">
|
||||
<div class="columns is-multiline">
|
||||
|
@ -56,6 +56,9 @@ export const configMock = {
|
||||
attribution: "© The OpenStreetMap Contributors",
|
||||
endpoint: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
},
|
||||
routing: {
|
||||
type: "OPENSTREETMAP",
|
||||
},
|
||||
},
|
||||
name: "Mobilizon",
|
||||
registrationsAllowlist: false,
|
||||
|
@ -117,6 +117,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do
|
||||
tiles: %{
|
||||
endpoint: Config.instance_maps_tiles_endpoint(),
|
||||
attribution: Config.instance_maps_tiles_attribution()
|
||||
},
|
||||
routing: %{
|
||||
type: Config.instance_maps_routing_type()
|
||||
}
|
||||
},
|
||||
resource_providers: Config.instance_resource_providers(),
|
||||
|
@ -106,16 +106,29 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do
|
||||
"""
|
||||
object :maps do
|
||||
field(:tiles, :tiles, description: "The instance's maps tiles configuration")
|
||||
field(:routing, :routing, description: "The instance's maps routing configuration")
|
||||
end
|
||||
|
||||
@desc """
|
||||
Instance tiles configuration
|
||||
Instance map tiles configuration
|
||||
"""
|
||||
object :tiles do
|
||||
field(:endpoint, :string, description: "The instance's tiles endpoint")
|
||||
field(:attribution, :string, description: "The instance's tiles attribution text")
|
||||
end
|
||||
|
||||
@desc """
|
||||
Instance map routing configuration
|
||||
"""
|
||||
object :routing do
|
||||
field(:type, :routing_type, description: "The instance's routing type")
|
||||
end
|
||||
|
||||
enum :routing_type do
|
||||
value(:openstreetmap, description: "Redirect to openstreetmap.org's direction endpoint")
|
||||
value(:google_maps, description: "Redirect to Google Maps's direction endpoint")
|
||||
end
|
||||
|
||||
@desc """
|
||||
Instance anonymous configuration
|
||||
"""
|
||||
|
@ -151,6 +151,10 @@ defmodule Mobilizon.Config do
|
||||
def instance_maps_tiles_attribution,
|
||||
do: Application.get_env(:mobilizon, :maps)[:tiles][:attribution]
|
||||
|
||||
@spec instance_maps_routing_type :: atom()
|
||||
def instance_maps_routing_type,
|
||||
do: Application.get_env(:mobilizon, :maps)[:routing][:type]
|
||||
|
||||
@spec anonymous_participation? :: boolean
|
||||
def anonymous_participation?,
|
||||
do: Application.get_env(:mobilizon, :anonymous)[:participation][:allowed]
|
||||
|
Loading…
Reference in New Issue
Block a user