Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
web-et-numerique
web-et-numerique-internet
data.grandlyon.com
web-portal
components
custom-apps
web-app
Commits
3e0645e5
Commit
3e0645e5
authored
Jan 17, 2020
by
ext.sopra.ncastejon
Browse files
Add the 3D layer. Available at zoom 14+
parent
b6d78546
Changes
6
Hide whitespace changes
Inline
Side-by-side
src/app/elasticsearch/services/elasticsearch.service.ts
View file @
3e0645e5
...
...
@@ -6,7 +6,6 @@ import { geosource, notificationMessages } from '../../../i18n/traductions';
import
{
ErrorService
}
from
'
../../core/services
'
;
import
{
APP_CONFIG
}
from
'
../../core/services/app-config.service
'
;
import
{
scopesResearch
}
from
'
../../shared/variables
'
;
// tslint:disable-next-line: max-line-length
import
{
Aggregation
,
ElasticsearchOptions
,
Filter
,
IElasticsearchResponse
,
IPostsESOptions
}
from
'
../models
'
;
@
Injectable
()
...
...
@@ -128,7 +127,7 @@ export class ElasticsearchService {
}
if
(
options
.
searchString
!==
''
)
{
const
searchString
=
this
.
escapeSpecialCharacters
(
options
.
searchString
,
options
.
fromAutocompletion
);
const
searchString
=
this
.
escapeSpecialCharacters
(
options
.
searchString
,
options
.
fromAutocompletion
,
'
AND
'
);
body
.
query
.
bool
[
'
must
'
]
=
[
{
query_string
:
{
...
...
@@ -622,7 +621,7 @@ export class ElasticsearchService {
/**
* Escape special characters except logical operators.
* */
escapeSpecialCharacters
(
searchString
,
fromAutocompletion
)
{
escapeSpecialCharacters
(
searchString
:
string
,
fromAutocompletion
:
boolean
,
joinOperator
=
'
+
'
)
{
let
escapedSearchString
=
''
;
/** If the request:
...
...
@@ -639,7 +638,7 @@ export class ElasticsearchService {
// We join each words with the '+' logical operator to accurate the results
const
words
=
escapedSearchString
.
split
(
/
\s
+/
);
escapedSearchString
=
words
.
join
(
'
+
'
);
escapedSearchString
=
words
.
join
(
`
${
joinOperator
}
`
);
/** If not all this, we don't escape the "()" (and will be interpreted as logical characters by ES)
* but we still do escape some other special characters
...
...
src/app/map/components/map.component.html
View file @
3e0645e5
...
...
@@ -24,6 +24,7 @@
</svg>
</ng-template>
</button>
<div
class=
"buttons has-addons column-content is-hidden-mobile"
>
<ng-container
*ngFor=
"let l of settings.baseLayers"
>
<button
class=
"button selectBase"
[disabled]=
"l.id === selectedBaseLayer.id"
...
...
@@ -34,6 +35,12 @@
</ng-container>
</div>
</div>
<div
class=
"button-3d"
*ngIf=
"displayButton3d"
>
<button
class=
"button"
[ngClass]=
"{'is-active': display3d}"
(click)=
"switch3DLayer()"
*ngIf=
"selectedBaseLayer.id===settings.baseLayers[0].id"
>
3D
</button>
</div>
<div
class=
"column is-narrow is-hidden-mobile"
>
<app-search-address
[optionsAutocomplete]=
"searchLocationResult"
(searchAddress)=
"searchAdress($event)"
(addressSelected)=
"flyTo($event)"
(clearAddress)=
"removeMarker()"
>
...
...
@@ -43,20 +50,17 @@
</div>
<div
class=
"geolocation-container"
>
<button
class=
"geolocation button is-medium"
type=
"button"
i18n-aria-label=
"@@dataset.detail.map.center"
aria-label=
"Center to my position"
(click)=
"centerToMyPosition()"
>
<button
class=
"geolocation button is-medium"
type=
"button"
i18n-aria-label=
"@@dataset.detail.map.center"
aria-label=
"Center to my position"
(click)=
"centerToMyPosition()"
>
</button>
</div>
<div
class=
"copy-map"
>
<input
type=
"text"
class=
"input"
id=
"mapUrlCopy"
i18n-aria-label=
"@@dataset.detail.map.share"
aria-label=
"Share the map"
[value]=
"mapUrl()"
#mapUrlElement
>
aria-label=
"Share the map"
[value]=
"mapUrl()"
#mapUrlElement
>
<button
class=
"button is-medium has-tooltip-left tooltip"
i18n-aria-label=
"@@dataset.detail.map.share"
aria-label=
"Share the map"
type=
"button"
(click)=
"copyMaplink(mapUrlElement)"
[attr.data-tooltip]=
"shareMessage"
>
aria-label=
"Share the map"
type=
"button"
(click)=
"copyMaplink(mapUrlElement)"
[attr.data-tooltip]=
"shareMessage"
>
<svg
xmlns=
"http://www.w3.org/2000/svg"
width=
"20"
height=
"20"
fill=
"none"
viewBox=
"0 0 20 20"
>
<path
fill=
"#818080"
fill-rule=
"evenodd"
d=
"M1.852 7.777a2.619 2.619 0 0 1 0-3.703l2.222-2.222a2.619 2.619 0 0 1 3.704 0l2.963 2.963a2.619 2.619 0 0 1 0 3.703l-.37.37.74.741.37-.37a2.619 2.619 0 0 1 3.704 0l2.963 2.963a2.619 2.619 0 0 1 0 3.704l-2.222 2.222a2.619 2.619 0 0 1-3.704 0L9.26 15.185a2.619 2.619 0 0 1 0-3.704l.37-.37-.74-.741-.37.37a2.619 2.619 0 0 1-3.704 0L1.852 7.777zm7.037-.37l-.74-.74a1.048 1.048 0 0 0-1.482 1.48l.74.742-.37.37a.524.524 0 0 1-.74 0L3.332 6.296a.524.524 0 0 1 0-.74l2.223-2.223a.524.524 0 0 1 .74 0L9.26 6.296a.524.524 0 0 1 0 .74l-.37.371zm2.222 5.185l-.37.37a.524.524 0 0 0 0 .742l2.963 2.962a.524.524 0 0 0 .74 0l2.223-2.222a.524.524 0 0 0 0-.74l-2.963-2.963a.524.524 0 0 0-.741 0l-.37.37.74.74a1.047 1.047 0 1 1-1.481 1.482l-.74-.74z"
...
...
src/app/map/components/map.component.scss
View file @
3e0645e5
...
...
@@ -61,6 +61,23 @@
border
:
none
;
}
.button-3d
{
position
:
absolute
;
top
:
50px
;
right
:
0
;
@media
screen
and
(
min-width
:
$tablet
)
{
left
:
0
;
}
&
>
.button.is-active
{
background-color
:
$blue-color
;
color
:
white
;
}
}
.copy-map
,
.geolocation-container
{
position
:
absolute
;
...
...
src/app/map/components/map.component.ts
View file @
3e0645e5
...
...
@@ -31,6 +31,7 @@ export class MapComponent implements OnInit, OnDestroy {
// Key of the lay to be displayed from baseLayers in environment files
selectedBaseLayer
=
this
.
settings
.
baseLayers
[
this
.
settings
.
defaultBaseLayer
];
displayPitchSlider
=
false
;
displayButton3d
=
false
;
display3d
=
false
;
// Attributes to manage the display of the pudate map button
...
...
@@ -39,8 +40,6 @@ export class MapComponent implements OnInit, OnDestroy {
previousDatasetId
:
string
;
availableLayers
:
string
[];
baseLayer3d
=
1
;
geolocation
=
false
;
searchLocationResult
=
[];
...
...
@@ -206,12 +205,30 @@ export class MapComponent implements OnInit, OnDestroy {
this
.
map
.
on
(
'
style.load
'
,
()
=>
{
this
.
_mapService
.
addLayers
();
});
this
.
map
.
on
(
'
zoomend
'
,
()
=>
{
if
(
this
.
map
.
getZoom
()
<
14
)
{
this
.
displayButton3d
=
false
;
if
(
this
.
map
.
getLayer
(
'
3d-layer
'
))
{
// this.display3d = false;
this
.
map
.
removeLayer
(
'
3d-layer
'
);
}
}
else
if
(
this
.
map
.
getZoom
()
>
14
)
{
this
.
displayButton3d
=
true
;
if
(
this
.
display3d
)
{
this
.
_mapService
.
switch3DLayer
();
}
}
});
}
}
switchLayer
(
baseLayer
)
{
this
.
selectedBaseLayer
=
baseLayer
;
this
.
_mapService
.
switchLayer
(
baseLayer
);
this
.
display3d
=
false
;
}
// [WARNING] This toggle only works with two base layers
...
...
@@ -222,6 +239,7 @@ export class MapComponent implements OnInit, OnDestroy {
this
.
selectedBaseLayer
=
this
.
settings
.
baseLayers
[
0
];
}
this
.
_mapService
.
switchLayer
(
this
.
selectedBaseLayer
);
this
.
display3d
=
false
;
}
// Looks for the language to be used, if not indicated in the url takes the navigator default language
...
...
@@ -239,7 +257,7 @@ export class MapComponent implements OnInit, OnDestroy {
switch3DLayer
()
{
if
(
this
.
map
.
isStyleLoaded
())
{
if
(
this
.
map
.
getSource
(
'
openmaptiles
'
))
{
if
(
this
.
map
.
getSource
(
'
3d-source
'
))
{
this
.
display3d
=
!
this
.
display3d
;
this
.
_mapService
.
switch3DLayer
();
}
...
...
src/app/map/services/map.service.ts
View file @
3e0645e5
...
...
@@ -8,12 +8,10 @@ import { Notification } from '../../core/models';
import
{
NotificationService
}
from
'
../../core/services
'
;
import
{
APP_CONFIG
}
from
'
../../core/services/app-config.service
'
;
import
{
DataType
,
MapOptions
}
from
'
../models/map-options
'
;
import
{
settings
}
from
'
../settings
'
;
@
Injectable
()
export
class
MapService
{
settings
=
settings
;
private
_map
:
mapboxgl
.
Map
;
private
url
:
string
;
selectedBaseLayer
:
any
;
...
...
@@ -22,11 +20,10 @@ export class MapService {
eventPopupAdded
=
false
;
mapIsConstructed
:
boolean
=
false
;
// Map
// Map
features colors
featureColor
:
string
=
'
#1d92ff
'
;
featureHoverColor
:
string
=
'
#E19190
'
;
featureHighlightedColor
:
string
=
'
#da322f
'
;
// Tomato color
visitedColor
:
string
=
'
#4668ab
'
;
hoveredFeatureId
:
string
;
highlightedFeatureId
:
string
;
...
...
@@ -46,8 +43,6 @@ export class MapService {
this
.
featureHighlightedColor
,
[
'
boolean
'
,
[
'
feature-state
'
,
'
hover
'
],
false
],
this
.
featureHoverColor
,
[
'
boolean
'
,
[
'
feature-state
'
,
'
visited
'
],
false
],
this
.
visitedColor
,
'
transparent
'
,
];
...
...
@@ -55,6 +50,7 @@ export class MapService {
private
_notificationService
:
NotificationService
,
)
{
}
// Init the map with basic options for controls, transform request etc...
createMap
(
mapOptions
:
MapOptions
,
url
:
string
,
baseLayer
:
any
,
options
?:
mapboxgl
.
MapboxOptions
):
mapboxgl
.
Map
{
...
...
@@ -108,10 +104,22 @@ export class MapService {
return
this
.
_map
;
}
// This adds 2 layers:
// - a WMS layer to display the visual part of the features (WMS service send a png or jpeg)
// - a data layer, created from a geojson or an MVT service. It is used for the features interaction (hover, click)
addLayers
()
{
this
.
addWMSLayer
();
// Add a geojson layer only if data from the metropole
// Add the 3d source. Constructed with MVT tiles from the 'fpc_fond_plan_communaut.fpctoit' dataset
const
domain
=
this
.
mapOptions
.
vectorService
.
url
.
split
(
'
wfs
'
)[
1
];
const
url
=
`
${
this
.
mapOptions
.
mvtUrl
}${
domain
}
?LAYERS=
fpc_fond_plan_communaut.fpctoit&map.imagetype=mvt&tilemode=gmap&tile={x}+{y}+{z}&mode=tile`
;
this
.
_map
.
addSource
(
'
3d-source
'
,
{
type
:
'
vector
'
,
tiles
:
[
url
],
});
// Add the data layer only if it comes from the Lyon Metropole
if
(
this
.
mapOptions
.
rasterService
.
url
.
includes
(
APP_CONFIG
.
backendUrls
.
wms
))
{
// There is two ways to add tha data layers: from a geojson or from a MVT url
if
(
!
this
.
mapOptions
.
isMVT
)
{
...
...
@@ -153,7 +161,7 @@ export class MapService {
// Set highlited style for the current feature
this
.
highlightedFeatureId
=
this
.
selectedFeature
;
this
.
changeFeatureState
(
this
.
highlightedFeatureId
,
{
visited
:
true
,
highlight
:
true
});
this
.
changeFeatureState
(
this
.
highlightedFeatureId
,
{
highlight
:
true
});
if
(
e
.
point
.
x
>
(
this
.
_map
.
getCanvas
().
width
-
400
))
{
// If the screen is not mobile the dataset data details panel push the map that is then smaller
...
...
@@ -206,7 +214,6 @@ export class MapService {
// Add the raster (WMS) layer
addWMSLayer
()
{
// ------------------- WMS Source & Layer -------------------
this
.
_map
.
addSource
(
'
wms-source
'
,
{
type
:
'
raster
'
,
tiles
:
[
...
...
@@ -225,10 +232,11 @@ export class MapService {
}
// Add the data layer (from geojson or MVT)
addDataLayer
()
{
let
layerOptions
=
{};
// Set the paint options depending the geometry type
// For 'Polygon' and 'MultiPolygon' features
if
(
this
.
mapOptions
.
dataType
.
isAreal
)
{
layerOptions
=
{
...
...
@@ -236,15 +244,12 @@ export class MapService {
paint
:
{
'
fill-color
'
:
this
.
COLOR_EXPRESSION
,
'
fill-opacity
'
:
0.7
,
'
fill-outline-color
'
:
[
'
case
'
,
[
'
boolean
'
,
[
'
feature-state
'
,
'
visited
'
],
false
],
'
white
'
,
'
transparent
'
,
],
'
fill-outline-color
'
:
'
transparent
'
,
},
};
}
// For 'Line' and 'MultiLine' features
if
(
this
.
mapOptions
.
dataType
.
isLinear
)
{
layerOptions
=
{
type
:
'
line
'
,
...
...
@@ -259,9 +264,8 @@ export class MapService {
};
}
// For "Point" features
if
(
this
.
mapOptions
.
dataType
.
isPunctual
)
{
// Add layer + style for the points
// Get paint options depending the dataset size
layerOptions
=
{
type
:
'
circle
'
,
paint
:
{
...
...
@@ -274,15 +278,11 @@ export class MapService {
'
circle-stroke-width
'
:
[
'
case
'
,
[
'
boolean
'
,
[
'
feature-state
'
,
'
highlight
'
],
false
],
5
,
[
'
boolean
'
,
[
'
feature-state
'
,
'
visited
'
],
false
],
1
,
0
,
],
'
circle-stroke-color
'
:
[
'
case
'
,
[
'
boolean
'
,
[
'
feature-state
'
,
'
highlight
'
],
false
],
this
.
featureHighlightedColor
,
[
'
boolean
'
,
[
'
feature-state
'
,
'
visited
'
],
false
],
'
white
'
,
'
transparent
'
,
],
},
...
...
@@ -294,11 +294,13 @@ export class MapService {
id
:
'
data-layer
'
,
source
:
'
vector-source
'
,
...
layerOptions
,
...(
this
.
mapOptions
.
isMVT
?
{
'
source-layer
'
:
this
.
mapOptions
.
vectorService
.
name
}
:
{}),
// if
not
MVT, this property is
not
needed
...(
this
.
mapOptions
.
isMVT
?
{
'
source-layer
'
:
this
.
mapOptions
.
vectorService
.
name
}
:
{}),
// if
from
MVT, this property is needed
});
// If not already done, add all the events listeners
if
(
!
this
.
eventPopupAdded
)
{
// Manage the cursor and feature state for point-features layer when mouse events
// Manage the cursor and feature state for data layer when mouse events
this
.
_map
.
on
(
'
mousemove
'
,
'
data-layer
'
,
(
e
)
=>
{
this
.
manageFeatureOnMouseMove
(
e
.
features
);
});
...
...
@@ -331,10 +333,8 @@ export class MapService {
);
}
});
this
.
eventPopupAdded
=
true
;
}
}
manageFeatureOnMouseMove
(
features
:
any
)
{
...
...
@@ -364,40 +364,25 @@ export class MapService {
}
switch3DLayer
()
{
if
(
!
this
.
_map
.
getLayer
(
'
building-3d
'
))
{
// Insert the layer beneath any symbol layer.
const
layers
=
this
.
_map
.
getStyle
().
layers
;
let
labelLayerId
;
for
(
let
i
=
0
;
i
<
layers
.
length
;
i
+=
1
)
{
if
(
layers
[
i
].
type
===
'
symbol
'
&&
layers
[
i
].
layout
[
'
text-field
'
])
{
labelLayerId
=
layers
[
i
].
id
;
break
;
}
}
if
(
!
this
.
_map
.
getLayer
(
'
3d-layer
'
))
{
this
.
_map
.
addLayer
(
{
id
:
'
building-3d
'
,
id
:
'
3d-layer
'
,
type
:
'
fill-extrusion
'
,
source
:
'
openmaptiles
'
,
'
source-layer
'
:
'
building
'
,
source
:
'
3d-source
'
,
'
source-layer
'
:
'
fpc_fond_plan_communaut.fpctoit
'
,
paint
:
{
'
fill-extrusion-color
'
:
'
hsla(40, 37%, 50%, 1)
'
,
'
fill-extrusion-color
'
:
'
#E0E4EF
'
,
'
fill-extrusion-height
'
:
{
property
:
'
render_height
'
,
property
:
'
htotale
'
,
type
:
'
identity
'
,
},
'
fill-extrusion-base
'
:
{
property
:
'
render_min_height
'
,
type
:
'
identity
'
,
},
'
fill-extrusion-opacity
'
:
0.8
,
'
fill-extrusion-opacity
'
:
0.7
,
},
},
labelLayerId
);
});
}
else
{
this
.
_map
.
removeLayer
(
'
building-3d
'
);
this
.
_map
.
removeLayer
(
'
3d-layer
'
);
}
}
...
...
@@ -430,7 +415,7 @@ export class MapService {
// Set highlited style for the current feature
this
.
highlightedFeatureId
=
this
.
selectedFeature
;
this
.
changeFeatureState
(
this
.
highlightedFeatureId
,
{
visited
:
true
,
highlight
:
true
});
this
.
changeFeatureState
(
this
.
highlightedFeatureId
,
{
highlight
:
true
});
const
pointCenter
=
selectedFeature
.
geometry
.
type
===
'
Point
'
?
selectedFeature
.
geometry
.
coordinates
:
centroid
(
selectedFeature
).
geometry
.
coordinates
;
...
...
@@ -448,31 +433,56 @@ export class MapService {
/*
* When the search value has been changed, we add a text expression
* that filters the features containing this text value in one of its properties.
* that filters the features containing this text value in
at least
one of its properties.
* If there is a match we:
* - decrease the opacity for the raster layer (WMS) which displays all the features
* - show the found features from our data layer (WMT or GeoJSON)
*/
filterBySearchValue
(
searchValue
:
string
,
properties
:
string
[])
{
const
filters
=
[];
if
(
searchValue
)
{
// Add the "in" expression (look for substring in string). To make it case insensitive, set
// both the text value and the property value to uppercase.
properties
.
forEach
((
property
)
=>
{
filters
.
push
([
'
in
'
,
[
'
upcase
'
,
searchValue
],
[
'
upcase
'
,
[
'
to-string
'
,
[
'
get
'
,
property
]]]]);
const
escapedSearchString
=
searchValue
.
replace
(
/
[\=
~><
\"\?
^
\$
{}
\(\)\|\&\:\!\/
[
\]\\]
/g
,
'
\\
$&
'
);
const
words
=
escapedSearchString
.
split
(
/
\s
+/
);
// Some basic explanations for the operators expression we use:
// - "all": returns true if all the conditions are true
// - "any": returns true if one of the the conditions are true
// - "in": can be used in many context, here it looks for a substring in a string. To make it case insensitive, set
// both the search value and the property value to uppercase with the operator 'upcase'.
// To learn more about it: https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions
// Here is the format of the expression, for example with 2 words:
// ['all',
// ['any',
// ['in', ['upcase', 'word1'], ['upcase', ['to-string', ['get', property1]]]],
// ['in', ['upcase', 'word1'], ['upcase', ['to-string', ['get', property2]]]],
// ['in', ['upcase', 'word1'], ['upcase', ['to-string', ['get', property3]]]]
// ],
// ['any',
// ['in', ['upcase', 'word2'], ['upcase', ['to-string', ['get', property1]]]],
// ['in', ['upcase', 'word2'], ['upcase', ['to-string', ['get', property2]]]],
// ['in', ['upcase', 'word2'], ['upcase', ['to-string', ['get', property3]]]]
// ],
// ]
const
anyFilter
=
[];
words
.
forEach
((
word
)
=>
{
const
propertiesFilters
=
[];
properties
.
forEach
((
property
)
=>
{
propertiesFilters
.
push
([
'
in
'
,
[
'
upcase
'
,
word
],
[
'
upcase
'
,
[
'
to-string
'
,
[
'
get
'
,
property
]]]]);
});
anyFilter
.
push
([
'
any
'
,
...
propertiesFilters
]);
});
// For each type layer if exists (point, line, polyon), add this filter and set a color to replace transparent
const
copyColor
=
[...
this
.
COLOR_EXPRESSION
]
copyColor
.
splice
(
copyColor
.
length
-
1
,
0
,
[
'
any
'
,
...
filters
],
'
green
'
);
// Once this search filter expression is done, we add it to the existing paint options (for hover, selected)
// of the data-layer
const
copyPaintOptions
=
[...
this
.
COLOR_EXPRESSION
];
copyPaintOptions
.
splice
(
copyPaintOptions
.
length
-
1
,
0
,
[
'
all
'
,
...
anyFilter
],
'
green
'
);
this
.
paintPropertyForLayer
(
'
data-layer
'
,
this
.
mapOptions
.
dataType
,
copy
Color
);
this
.
paintPropertyForLayer
(
'
data-layer
'
,
this
.
mapOptions
.
dataType
,
copy
PaintOptions
);
this
.
_map
.
setPaintProperty
(
'
wms-layer
'
,
'
raster-opacity
'
,
0.5
);
}
else
{
// If value is empty, remove the
filter
and set the opacity for the raster layer back to 1.
// If value is empty, remove the
search expression
and set the opacity for the raster layer back to 1.
this
.
paintPropertyForLayer
(
'
data-layer
'
,
this
.
mapOptions
.
dataType
,
this
.
COLOR_EXPRESSION
);
this
.
_map
.
setPaintProperty
(
'
wms-layer
'
,
'
raster-opacity
'
,
1
);
...
...
src/app/map/settings.ts
View file @
3e0645e5
export
const
settings
=
{
maxDisplayFeatures
:
500
,
// Map
defaultBaseLayer
:
0
,
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment