diff --git a/internal/mocks/mocks.go b/internal/mocks/mocks.go index 514bd7e2b8bd7327aae3faa213408f548230e621..d5ca06a778648f6859f109787f5989eaf074c93b 100644 --- a/internal/mocks/mocks.go +++ b/internal/mocks/mocks.go @@ -7,7 +7,7 @@ import ( "os" "strconv" - "forge.grandlyon.com/systemes-dinformation/project-template/sdk-go/pkg/middlewares" + "forge.grandlyon.com/gestion-des-assemblees/elections/pkg/middlewares" ) const literralContentType = "Content-Type" diff --git a/internal/rootmux/rootmux.go b/internal/rootmux/rootmux.go index 9d09f7088a1a1a2d71a20990b550850be6f8a73a..6bbd0122f54a73f929f87cb5ff93594458d00652 100644 --- a/internal/rootmux/rootmux.go +++ b/internal/rootmux/rootmux.go @@ -6,7 +6,7 @@ import ( "forge.grandlyon.com/gestion-des-assemblees/elections/internal/auth" "forge.grandlyon.com/gestion-des-assemblees/elections/internal/models" - "forge.grandlyon.com/systemes-dinformation/project-template/sdk-go/pkg/middlewares" + "forge.grandlyon.com/gestion-des-assemblees/elections/pkg/middlewares" "forge.grandlyon.com/systemes-dinformation/project-template/sdk-go/pkg/common" ) @@ -47,6 +47,6 @@ func CreateRootMux(port int, staticDir string) RootMux { mainMux.Handle("/", middlewares.NoCache(http.FileServer(&common.FallBackWrapper{Assets: http.Dir(staticDir)}))) // Put it together into the main handler mux := http.NewServeMux() - mux.Handle(hostname+"/", middlewares.WebSecurity(mainMux, "*."+hostname+":*", false)) + mux.Handle(hostname+"/", middlewares.WebSecurity(mainMux, "*."+hostname+":* *.mapbox.com *.grandlyon.com ", false)) return RootMux{mux, &m} } diff --git a/main.go b/main.go index 3520591433848b7e54888af26407ddd9bceca06a..21814b981479b09d827296b57495946aea351065 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "syscall" "time" - "forge.grandlyon.com/systemes-dinformation/project-template/sdk-go/pkg/middlewares" + "forge.grandlyon.com/gestion-des-assemblees/elections/pkg/middlewares" "forge.grandlyon.com/systemes-dinformation/project-template/sdk-go/pkg/tokens" "forge.grandlyon.com/gestion-des-assemblees/elections/internal/mocks" diff --git a/pkg/middlewares/middlewares.go b/pkg/middlewares/middlewares.go new file mode 100644 index 0000000000000000000000000000000000000000..d7a68e6e42e8f6a73ef27ce9a1bb9762f5fa7d16 --- /dev/null +++ b/pkg/middlewares/middlewares.go @@ -0,0 +1,51 @@ +package middlewares + +import ( + "fmt" + "net/http" + "strconv" +) + +// Cors enables CORS Request on server (for development purposes) +func Cors(next http.Handler, allowedOrigin string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", allowedOrigin) + w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, PROPFIND, MKCOL, MOVE, COPY") + w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, XSRF-TOKEN, Authorization, Depth, Destination") + w.Header().Set("Access-Control-Allow-Credentials", "true") + next.ServeHTTP(w, req) + }) +} + +// WebSecurity adds good practices security headers on http responses +func WebSecurity(next http.Handler, source string, allowEvalInlineScript bool) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Strict-Transport-Security", "max-age=63072000") + var inline string + if allowEvalInlineScript { + inline = "'unsafe-inline' 'unsafe-eval'" + } + w.Header().Set("Content-Security-Policy", fmt.Sprintf("default-src %[1]v 'self'; img-src %[1]v 'self' data: blob:; script-src %[1]v 'self' %[2]v; style-src %[1]v 'self' 'unsafe-inline'; frame-src %[1]v; frame-ancestors %[1]v; worker-src blob: ;child-src blob: ;", source, inline)) + //w.Header().Set("X-Frame-Options", "SAMEORIGIN") // Works fine with chrome but is not obsoleted by frame-src in firefox 72.0.2 + w.Header().Set("X-XSS-Protection", "1; mode=block") + w.Header().Set("Referrer-Policy", "strict-origin") + w.Header().Set("X-Content-Type-Options", "nosniff") + next.ServeHTTP(w, req) + }) +} + +// NoCache disable caching +func NoCache(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Cache-Control", "no-store, must-revalidate") + next.ServeHTTP(w, req) + }) +} + +// GetFullHostname returns the full hostname of the server +func GetFullHostname(hostname string, port int) string { + if port == 80 || port == 443 { + return "https://" + hostname + } + return "https://" + hostname + ":" + strconv.Itoa(port) +} diff --git a/web/assets/mapbox/vector.json b/web/assets/mapbox/vector.json new file mode 100644 index 0000000000000000000000000000000000000000..c6a026345b88a4c930fd40017c79ac27664c7938 --- /dev/null +++ b/web/assets/mapbox/vector.json @@ -0,0 +1,1106 @@ +{ + "version": 8, + "name": "data", + "metadata": { + "mapbox:autocomposite": false, + "mapbox:type": "template", + "maputnik:renderer": "mbgljs", + "openmaptiles:version": "3.x", + "openmaptiles:mapbox:owner": "openmaptiles", + "openmaptiles:mapbox:source:url": "mapbox://openmaptiles.4qljc88t" + }, + "sources": { + "openmaptiles": { + "type": "vector", + "tiles": [ + "https://openmaptiles.data.grandlyon.com/data/v3/{z}/{x}/{y}.pbf" + ], + "minzoom": 0, + "maxzoom": 22, + "attribution": "© <a href='https://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors" + } + }, + "sprite": "https://minio.alpha.grandlyon.com/mapbox-assets/mapbox-sprite", + "glyphs": "https://minio.alpha.grandlyon.com/mapbox-glyphs/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#f1f3f8" + } + }, + { + "id": "landuse-residential", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landuse", + "filter": [ + "all", + ["==", "$type", "Polygon"], + ["==", "class", "residential"] + ], + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "rgba(237, 238, 234, 1)", + "fill-opacity": 0.7 + } + }, + { + "id": "landcover", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "filter": ["match", ["get", "class"], ["wood", "grass"], true, false], + "paint": { + "fill-color": "#C8E8B6" + } + }, + { + "id": "landcover_grass", + "type": "fill", + "source": "openmaptiles", + "minzoom": 11, + "source-layer": "landcover", + "filter": ["==", "class", "grass"], + "paint": { + "fill-pattern": "grass" + } + }, + { + "id": "landcover_wood", + "type": "fill", + "minzoom": 11, + "source": "openmaptiles", + "source-layer": "landcover", + "filter": ["match", ["get", "class"], ["wood"], true, false], + "paint": { + "fill-pattern": "wood" + } + }, + { + "id": "water", + "type": "fill", + "source": "openmaptiles", + "source-layer": "water", + "filter": ["==", "$type", "Polygon"], + "paint": { + "fill-color": "#accce9", + "fill-outline-color": "hsla(209, 58%, 79%, 0.7)", + "fill-pattern": "water", + "fill-opacity": 1 + } + }, + { + "id": "landcover-ice-shelf", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "filter": ["==", "subclass", "ice_shelf"], + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "hsl(47, 26%, 88%)", + "fill-opacity": 0.8 + } + }, + { + "id": "landcover-glacier", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "filter": ["==", "subclass", "glacier"], + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-color": "hsl(47, 22%, 94%)", + "fill-opacity": { + "base": 1, + "stops": [ + [0, 1], + [8, 0.5] + ] + } + } + }, + { + "id": "landcover_sand", + "type": "fill", + "metadata": {}, + "source": "openmaptiles", + "source-layer": "landcover", + "filter": ["all", ["in", "class", "sand"]], + "paint": { + "fill-antialias": false, + "fill-color": "rgba(232, 214, 38, 1)", + "fill-opacity": 0.3 + } + }, + { + "id": "landuse", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landuse", + "minzoom": 15, + "filter": [ + "match", + ["get", "class"], + ["airport", "hospital", "school"], + true, + false + ], + "layout": {}, + "paint": { + "fill-color": [ + "match", + ["get", "class"], + ["airport"], + "#e0e4f0", + ["hospital"], + "#e0e4f0", + ["school"], + "#e0e4f0", + "hsla(0, 0%, 0%, 0)" + ], + "fill-opacity": ["interpolate", ["linear"], ["zoom"], 5, 0, 6, 1], + "fill-outline-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "landuse_overlay_national_park", + "type": "fill", + "source": "openmaptiles", + "source-layer": "landcover", + "filter": ["==", "class", "national_park"], + "paint": { + "fill-color": "#E1EBB0", + "fill-opacity": { + "base": 1, + "stops": [ + [5, 0], + [9, 0.75] + ] + } + } + }, + { + "id": "park_outline", + "type": "line", + "source": "openmaptiles", + "source-layer": "park", + "layout": {}, + "paint": { + "line-color": "rgba(159, 183, 118, 0.69)", + "line-dasharray": [0.5, 1] + } + }, + { + "id": "waterway-tunnel", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "tunnel"] + ], + "paint": { + "line-color": "hsl(205, 56%, 73%)", + "line-width": { + "base": 1.4, + "stops": [ + [8, 1], + [20, 2] + ] + }, + "line-opacity": 1, + "line-gap-width": { + "stops": [ + [12, 0], + [20, 6] + ] + }, + "line-dasharray": [3, 3] + } + }, + { + "id": "waterway", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["!in", "brunnel", "tunnel", "bridge"] + ], + "layout": { + "visibility": "none" + }, + "paint": { + "line-color": "hsl(205, 56%, 73%)", + "line-width": { + "base": 1.4, + "stops": [ + [8, 1], + [20, 8] + ] + }, + "line-opacity": 1 + } + }, + { + "id": "tunnel_railway_transit", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 0, + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "tunnel"], + ["==", "class", "transit"] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "rgba(170, 170, 170, 1)", + "line-opacity": { + "base": 1, + "stops": [ + [11, 0], + [16, 1] + ] + }, + "line-dasharray": [3, 3] + } + }, + { + "id": "building", + "type": "fill", + "source": "openmaptiles", + "source-layer": "building", + "minzoom": 15, + "layout": {}, + "paint": { + "fill-color": "#e0e4f0", + "fill-opacity": ["interpolate", ["linear"], ["zoom"], 13.5, 0, 14, 1], + "fill-outline-color": "#f1f3f8" + } + }, + { + "id": "housenumber", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "housenumber", + "minzoom": 17, + "filter": ["==", "$type", "Point"], + "layout": { + "text-field": "{housenumber}", + "text-size": 10, + "text-font": ["titiliumweb-regular"] + }, + "paint": { + "text-color": "rgba(178, 178, 178, 1)" + } + }, + { + "id": "road_bridge_area", + "type": "fill", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "Polygon"], + ["in", "brunnel", "bridge"] + ], + "layout": {}, + "paint": { + "fill-color": "hsl(47, 26%, 88%)", + "fill-opacity": 0.5 + } + }, + { + "id": "road_path", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["in", "class", "path", "track"] + ], + "layout": { + "line-cap": "square", + "line-join": "bevel", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-dasharray": [1, 1], + "line-width": { + "base": 1.55, + "stops": [ + [4, 0.25], + [20, 10] + ] + } + } + }, + { + "id": "road_minor", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["in", "class", "minor", "service"] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "hsl(0, 0%, 97%)", + "line-width": { + "base": 1.55, + "stops": [ + [4, 0.25], + [20, 30] + ] + } + } + }, + { + "id": "tunnel_minor", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "tunnel"], + ["==", "class", "minor_road"] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-width": { + "base": 1.55, + "stops": [ + [4, 0.25], + [20, 30] + ] + }, + "line-dasharray": [0.36, 0.18] + } + }, + { + "id": "tunnel_major", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "tunnel"], + ["in", "class", "primary", "secondary", "tertiary", "trunk"] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-width": { + "base": 1.4, + "stops": [ + [6, 0.5], + [20, 30] + ] + }, + "line-dasharray": [0.28, 0.14] + } + }, + { + "id": "aeroway-area", + "type": "fill", + "metadata": { + "mapbox:group": "1444849345966.4436" + }, + "source": "openmaptiles", + "source-layer": "aeroway", + "minzoom": 4, + "filter": [ + "all", + ["==", "$type", "Polygon"], + ["in", "class", "runway", "taxiway"] + ], + "layout": { + "visibility": "visible" + }, + "paint": { + "fill-opacity": { + "base": 1, + "stops": [ + [13, 0], + [14, 1] + ] + }, + "fill-color": "rgba(255, 255, 255, 1)" + } + }, + { + "id": "aeroway-taxiway", + "type": "line", + "metadata": { + "mapbox:group": "1444849345966.4436" + }, + "source": "openmaptiles", + "source-layer": "aeroway", + "minzoom": 12, + "filter": [ + "all", + ["in", "class", "taxiway"], + ["==", "$type", "LineString"] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-width": { + "base": 1.5, + "stops": [ + [12, 1], + [17, 10] + ] + }, + "line-opacity": 1 + } + }, + { + "id": "aeroway-runway", + "type": "line", + "metadata": { + "mapbox:group": "1444849345966.4436" + }, + "source": "openmaptiles", + "source-layer": "aeroway", + "minzoom": 4, + "filter": [ + "all", + ["in", "class", "runway"], + ["==", "$type", "LineString"] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-width": { + "base": 1.5, + "stops": [ + [11, 4], + [17, 50] + ] + }, + "line-opacity": 1 + } + }, + { + "id": "road_trunk_primary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["in", "class", "trunk", "primary"] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.4, + "stops": [ + [6, 0.5], + [20, 30] + ] + } + } + }, + { + "id": "road_secondary_tertiary", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["in", "class", "secondary", "tertiary"] + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.4, + "stops": [ + [6, 0.5], + [20, 20] + ] + } + } + }, + { + "id": "road_major_motorway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "class", "motorway"] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "hsl(0, 0%, 100%)", + "line-width": { + "base": 1.4, + "stops": [ + [8, 1], + [16, 10] + ] + }, + "line-offset": 0 + } + }, + { + "id": "road_oneway", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "filter": ["all", ["==", "oneway", 1]], + "layout": { + "symbol-placement": "line", + "icon-image": "oneway", + "symbol-spacing": 200, + "icon-padding": 2, + "icon-rotation-alignment": "map", + "icon-rotate": 0, + "icon-size": { + "stops": [ + [15, 0.5], + [19, 1] + ] + } + }, + "paint": { + "icon-opacity": 0.5 + } + }, + { + "id": "road_oneway_opposite", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation", + "minzoom": 15, + "filter": ["all", ["==", "oneway", -1]], + "layout": { + "symbol-placement": "line", + "icon-image": "oneway", + "symbol-spacing": 200, + "icon-padding": 2, + "icon-rotation-alignment": "map", + "icon-rotate": 180, + "icon-size": { + "stops": [ + [15, 0.5], + [19, 1] + ] + } + }, + "paint": { + "icon-opacity": 0.5 + } + }, + { + "id": "railway-transit", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "class", "transit"], + ["!=", "brunnel", "tunnel"] + ], + "layout": { + "visibility": "none" + }, + "paint": { + "line-color": "hsl(34, 12%, 66%)", + "line-opacity": { + "base": 1, + "stops": [ + [11, 0], + [16, 1] + ] + } + } + }, + { + "id": "railway", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": ["==", "class", "rail"], + "layout": { + "visibility": "visible" + }, + "paint": { + "line-color": "rgba(203, 203, 203, 1)", + "line-opacity": { + "base": 1, + "stops": [ + [11, 0], + [16, 1] + ] + } + } + }, + { + "id": "waterway-bridge-case", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "bridge"] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "#bbbbbb", + "line-width": { + "base": 1.6, + "stops": [ + [12, 0.5], + [20, 10] + ] + }, + "line-gap-width": { + "base": 1.55, + "stops": [ + [4, 0.25], + [20, 30] + ] + } + } + }, + { + "id": "waterway-bridge", + "type": "line", + "source": "openmaptiles", + "source-layer": "waterway", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "bridge"] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "hsl(205, 56%, 73%)", + "line-width": { + "base": 1.55, + "stops": [ + [4, 0.25], + [20, 30] + ] + } + } + }, + { + "id": "bridge_minor case", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "bridge"], + ["==", "class", "minor_road"] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "#dedede", + "line-width": { + "base": 1.6, + "stops": [ + [12, 0.5], + [20, 10] + ] + }, + "line-gap-width": { + "base": 1.55, + "stops": [ + [4, 0.25], + [20, 30] + ] + } + } + }, + { + "id": "bridge_major case", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "bridge"], + ["in", "class", "primary", "secondary", "tertiary", "trunk"] + ], + "layout": { + "line-cap": "butt", + "line-join": "miter" + }, + "paint": { + "line-color": "#dedede", + "line-width": { + "base": 1.6, + "stops": [ + [12, 0.5], + [20, 10] + ] + }, + "line-gap-width": { + "base": 1.55, + "stops": [ + [4, 0.25], + [20, 30] + ] + } + } + }, + { + "id": "bridge_minor", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "bridge"], + ["==", "class", "minor_road"] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#efefef", + "line-width": { + "base": 1.55, + "stops": [ + [4, 0.25], + [20, 30] + ] + } + } + }, + { + "id": "bridge_major", + "type": "line", + "source": "openmaptiles", + "source-layer": "transportation", + "filter": [ + "all", + ["==", "$type", "LineString"], + ["==", "brunnel", "bridge"], + ["in", "class", "primary", "secondary", "tertiary", "trunk"] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "#fff", + "line-width": { + "base": 1.4, + "stops": [ + [6, 0.5], + [20, 30] + ] + } + } + }, + { + "id": "admin_sub", + "type": "line", + "source": "openmaptiles", + "source-layer": "boundary", + "filter": ["in", "admin_level", 4, 6, 8], + "layout": { + "visibility": "none" + }, + "paint": { + "line-color": "hsla(0, 0%, 60%, 0.5)", + "line-dasharray": [2, 1] + } + }, + { + "id": "admin_country", + "type": "line", + "source": "openmaptiles", + "source-layer": "boundary", + "filter": [ + "all", + ["<=", "admin_level", 2], + ["==", "$type", "LineString"] + ], + "layout": { + "line-cap": "round", + "line-join": "round" + }, + "paint": { + "line-color": "hsl(0, 0%, 60%)", + "line-width": { + "base": 1.3, + "stops": [ + [3, 0.5], + [22, 15] + ] + } + } + }, + { + "id": "poi_label", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 14, + "filter": ["all", ["==", "$type", "Point"], ["==", "rank", 1]], + "layout": { + "text-size": 11, + "text-font": ["titiliumweb-regular"], + "visibility": "visible", + "text-offset": [0, 0.5], + "icon-size": 1, + "text-anchor": "top", + "text-field": "{name:latin}\n{name:nonlatin}", + "text-max-width": 8 + }, + "paint": { + "text-color": "#818080", + "text-halo-width": 1, + "text-halo-color": "rgba(255,255,255,0.75)", + "text-halo-blur": 1 + } + }, + { + "id": "airport-label", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "aerodrome_label", + "minzoom": 10, + "filter": ["all", ["has", "iata"]], + "layout": { + "text-size": 11, + "text-font": ["titiliumweb-regular"], + "visibility": "visible", + "text-offset": [0, 0.5], + "icon-size": 1, + "text-anchor": "top", + "text-field": "{name:latin}\n{name:nonlatin}", + "text-max-width": 8 + }, + "paint": { + "text-color": "#666", + "text-halo-width": 1, + "text-halo-color": "rgba(255,255,255,0.75)", + "text-halo-blur": 1 + } + }, + { + "id": "road_major_label", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "transportation_name", + "filter": ["==", "$type", "LineString"], + "layout": { + "symbol-placement": "line", + "text-field": "{name:latin} {name:nonlatin}", + "text-font": ["titiliumweb-regular"], + "text-transform": "uppercase", + "text-letter-spacing": 0.1, + "text-size": { + "base": 1.4, + "stops": [ + [10, 8], + [20, 14] + ] + }, + "text-rotation-alignment": "map" + }, + "paint": { + "text-color": "#333744", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-width": 2 + } + }, + { + "id": "place_label_other", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "minzoom": 8, + "filter": [ + "all", + ["==", "$type", "Point"], + ["!in", "class", "city", "state", "country", "continent"] + ], + "layout": { + "text-field": "{name:latin}\n{name:nonlatin}", + "text-font": ["titiliumweb-regular"], + "text-max-width": 6, + "text-size": { + "stops": [ + [6, 10], + [12, 14] + ] + }, + "visibility": "visible", + "text-anchor": "center" + }, + "paint": { + "text-color": "rgba(129, 128, 128, 1)", + "text-halo-color": "hsl(0, 0%, 100%)", + "text-halo-blur": 0, + "text-halo-width": 2 + } + }, + { + "id": "place_label_city", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "maxzoom": 16, + "filter": ["all", ["==", "$type", "Point"], ["==", "class", "city"]], + "layout": { + "text-field": "{name:latin}\n{name:nonlatin}", + "text-font": ["titiliumweb-regular"], + "text-max-width": 10, + "text-size": { + "stops": [ + [3, 14], + [8, 16] + ] + }, + "text-justify": "center" + }, + "paint": { + "text-color": "rgba(51, 55, 68, 1)", + "text-halo-color": "hsla(0, 0%, 100%, 0.75)", + "text-halo-blur": 0, + "text-halo-width": 2 + } + }, + { + "id": "country_label-other", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "maxzoom": 12, + "filter": [ + "all", + ["==", "$type", "Point"], + ["==", "class", "country"], + ["!has", "iso_a2"] + ], + "layout": { + "text-field": "{name:latin}", + "text-font": ["titiliumweb-regular"], + "text-max-width": 10, + "text-size": { + "stops": [ + [3, 12], + [8, 22] + ] + }, + "visibility": "visible" + }, + "paint": { + "text-color": "hsl(0, 0%, 13%)", + "text-halo-color": "rgba(255,255,255,0.75)", + "text-halo-width": 2, + "text-halo-blur": 0 + } + }, + { + "id": "country_label", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "place", + "maxzoom": 12, + "filter": [ + "all", + ["==", "$type", "Point"], + ["==", "class", "country"], + ["has", "iso_a2"] + ], + "layout": { + "text-field": "{name:latin}", + "text-font": ["titiliumweb-regular"], + "text-max-width": 10, + "text-size": { + "stops": [ + [3, 12], + [8, 22] + ] + }, + "visibility": "visible" + }, + "paint": { + "text-color": "hsl(0, 0%, 13%)", + "text-halo-color": "rgba(255,255,255,0.75)", + "text-halo-width": 2, + "text-halo-blur": 0 + } + } + ], + "id": "klokantech-basic", + "owner": "" +} diff --git a/web/components/visualization/results-map.js b/web/components/visualization/results-map.js new file mode 100644 index 0000000000000000000000000000000000000000..87c847bab48f11093e457e79ed60c087000641a6 --- /dev/null +++ b/web/components/visualization/results-map.js @@ -0,0 +1,87 @@ +// Imports +import * as PartyModel from "/services/model/party-model.js"; +import * as AreaModel from "/services/model/area-model.js"; + +export async function mount(parent) { + const mapComponent = new MapComponent(parent); + return mapComponent; +} + +class MapComponent { + constructor(parent) { + this.parent = parent; + this.PartyModel = PartyModel.getPartyModel(); + this.AreaModel = AreaModel.getAreaModel(); + } + + async displayMapAreas() { + console.log("mount area map") + await this.initMap("/assets/maps/area.json", this.colorAreas); + } + + async displayMapSections() { + console.log("mount section map") + await this.initMap("/assets/maps/section.json", this.colorSections); + } + + async initMap(mapFile, colorationFunction) { + // Create datasource + const request = new XMLHttpRequest(); + request.open("GET", mapFile, false); + request.send(null); + let dataSource = JSON.parse(request.responseText); + + // Add parties and colors to datasource + dataSource = await colorationFunction(dataSource); + + document.getElementById("map-component").innerHTML = ""; + // Create a popup, but don't add it to the map yet. + let popup = new mapboxgl.Popup({ + closeButton: false, + }); + + const map = new mapboxgl.Map({ + container: "map-component", // container id + style: "/assets/mapbox/vector.json", // stylesheet location + center: [4.9, 45.75], // starting position [lng, lat] + zoom: 9.7, // starting zoom + }); + + // map.on("load", function () { + // map.addSource("data-source", { + // type: "geojson", + // data: dataSource, + // }); + + // map.addLayer( + // { + // id: "winners-fills", + // type: "fill", + // source: "data-source", + // layout: {}, + // paint: { + // "fill-color": { type: "identity", property: "color" }, + // "fill-opacity": 0.5, + // "fill-outline-color": "black", + // }, + // }, + // "place_label_city" + // ); + + // map.addLayer({ + // id: "winners-names", + // type: "symbol", + // source: "data-source", + // filter: ["all"], + // layout: { + // "text-field": "{partyName}", + // "text-font": ["titiliumweb-regular"], + // }, + // }); + // }); + } + + async colorAreas(dataSource) {} + + async colorSections(dataSource) {} +} diff --git a/web/components/visualization/results-zone.js b/web/components/visualization/results-zone.js index 022bd02f36221f9d6cb6789c48a96c1abd60bb63..05be9f432691ce8b4760ea12ff060ec5713b3f62 100644 --- a/web/components/visualization/results-zone.js +++ b/web/components/visualization/results-zone.js @@ -6,6 +6,7 @@ import * as AreaModel from "/services/model/area-model.js"; import * as Scroller from "/services/common/scroller.js"; import * as ResultsFlow from "/components/visualization/results-flow.js"; import * as ResultsDetaileds from "/components/visualization/results-detaileds.js"; +import * as ResultsMap from "/components/visualization/results-map.js"; export async function mount(where, parent) { const resultZoneComponent = new ResultZoneComponent(parent); @@ -91,6 +92,7 @@ class ResultZoneComponent { `; this.ResultsFlow = await ResultsFlow.mount(this); this.ResultsDetaileds = await ResultsDetaileds.mount(this); + this.ResultsMap = await ResultsMap.mount(this); this.scroller = Scroller.scrollInit( "news-flow", document.getElementById("auto-scroll") @@ -136,6 +138,9 @@ class ResultZoneComponent { let resultHandler = this; document.getElementById("map-section").parentElement.className = "column is-full"; + console.log(this.ResultsMap); + if (this.parent.zone === "areas") this.ResultsMap.displayMapAreas(); + else this.ResultsMap.displayMapSections(); document.getElementById("zoom-map").addEventListener("click", function () { resultHandler.unZoom(); }); @@ -215,6 +220,9 @@ class ResultZoneComponent { <i class="fa fa-expand-arrows-alt"></i> </span>`; + if (this.parent.zone === "areas") this.ResultsMap.displayMapAreas(); + else this.ResultsMap.displayMapSections(); + this.handleDom(); } diff --git a/web/components/visualization/visualization-section.js b/web/components/visualization/visualization-section.js index 0c3d7fa6c07c9489b32832ee82685886081d7ee4..a58d6db7dce717739dc3f32d397299842e1d6640 100644 --- a/web/components/visualization/visualization-section.js +++ b/web/components/visualization/visualization-section.js @@ -104,12 +104,14 @@ class ResultComponent { await this.hideGeneralSection(); document.getElementById("sections").setAttribute("class", ""); document.getElementById("areas").setAttribute("class", "is-active"); + this.resultsZone.ResultsMap.displayMapAreas(); }); document.getElementById("sections").addEventListener("click", async () => { resultHandler.zone = "sections"; await this.hideGeneralSection(); document.getElementById("areas").setAttribute("class", ""); document.getElementById("sections").setAttribute("class", "is-active"); + this.resultsZone.ResultsMap.displayMapSections(); }); let radioButtons = document.getElementsByName("filter"); diff --git a/web/index.html b/web/index.html index 526f42ee3e88b1ab523602b60f30448fa546cbac..eddde1d663bf645c1ccbada17df42fd8430369a4 100644 --- a/web/index.html +++ b/web/index.html @@ -1,4 +1,5 @@ <!DOCTYPE html> + <html lang="en" class="has-navbar-fixed-top"> <head> <title>Elections</title> @@ -9,6 +10,11 @@ <link rel="stylesheet" href="assets/bulma.min.css" /> <link rel="stylesheet" href="assets/animate.css" /> <link rel="stylesheet" href="style.css" /> + <script src="https://api.mapbox.com/mapbox-gl-js/v1.11.1/mapbox-gl.js"></script> + <link + rel="stylesheet" + href="https://api.mapbox.com/mapbox-gl-js/v1.11.1/mapbox-gl.css" + /> <script defer src="assets/fontawesome/brands.min.js"></script> <script defer src="assets/fontawesome/solid.min.js"></script> <script defer src="assets/fontawesome/fontawesome.min.js"></script> @@ -16,7 +22,12 @@ </head> <body> <!-- Navbar --> - <nav id="navbar" class="navbar is-dark is-fixed-top" role="navigation" aria-label="main navigation"></nav> + <nav + id="navbar" + class="navbar is-dark is-fixed-top" + role="navigation" + aria-label="main navigation" + ></nav> <!-- Main content--> <section class="section" id="main" style="margin-bottom: 230px;"></section> @@ -29,7 +40,12 @@ <div class="navbar-brand"> <div class="navbar-item"> <div class="buttons"> - <a class="button is-danger" href="https://forge.grandlyon.com/gestion-des-assemblees/elections" target="_blank" rel="noopener noreferrer"> + <a + class="button is-danger" + href="https://forge.grandlyon.com/gestion-des-assemblees/elections" + target="_blank" + rel="noopener noreferrer" + > <span class="icon"> <svg class="svg-inline--fa fa-gitlab fa-w-16" @@ -45,8 +61,8 @@ <path fill="currentColor" d="M105.2 24.9c-3.1-8.9-15.7-8.9-18.9 0L29.8 199.7h132c-.1 0-56.6-174.8-56.6-174.8zM.9 287.7c-2.6 8 .3 16.9 7.1 22l247.9 184-226.2-294zm160.8-88l94.3 294 94.3-294zm349.4 88l-28.8-88-226.3 294 247.9-184c6.9-5.1 9.7-14 7.2-22zM425.7 24.9c-3.1-8.9-15.7-8.9-18.9 0l-56.6 174.8h132z" - ></path></svg - > + ></path> + </svg> </span> </a> </div> diff --git a/web/style.css b/web/style.css index 2a8b9048b43479c80e73b58e3835b2177869c862..449b49759f67645f193e595441a1729b5e8fed26 100644 --- a/web/style.css +++ b/web/style.css @@ -211,4 +211,12 @@ select { .clickable { cursor: pointer; -} \ No newline at end of file +} + +#map-section { + height: 70vh; +} + +#map-component { + height: 100%; +}