Widget Tricks - ods-map: Refine & Zoom

Map as a selector

In this example, the french administrative regions are displayed first and the user click refines on the departments dataset. On the right section you can observe the applied filter on departments context and the list of departments returned. The ‘regions’ context is not affected and is not filtered. Note that it would also be possible to apply the filter on regions context, to do so, please have a look to the next resource of this page.

If your layer is displayed as raw or aggregation, you can configure a layer so that a click on an item triggers a refine on another context, using refineOnClickContext. One or more contexts can be defined:

<div class="row ods-box">
    <ods-dataset-context context="regions,depts"
                         regions-dataset="regions-et-collectivites-doutre-mer-france"
                         regions-domain="userclub"
                         depts-dataset="departements-et-collectivites-doutre-mer-france"
                         depts-domain="userclub">
        <div class="col-md-8">
            <ods-map style="height:560px" scroll-wheel-zoom="false">
                <ods-map-layer context="regions"
                               refine-on-click-context="depts"
                               refine-on-click-depts-map-field="reg_code"
                               refine-on-click-depts-context-field="reg_code"
                               refine-on-click-depts-replace-refine="true"
                               color="#3D3D3D">
                </ods-map-layer>
            </ods-map>
        </div>
        <div class="col-md-4">
            <div ng-if="!depts.parameters['refine.reg_code']">
                <h3 style="font-weight:600; margin-top: 40px">
                    Selectionnez une région sur la carte
                </h3>
            </div>
            <div ng-if="depts.parameters['refine.reg_code']">
                  <span class="ods-button" ng-click="depts.parameters = {};">
                      Supprimer le filtre
                  </span>
                <h3>Filtre actif : code_reg = {{ depts.parameters['refine.reg_code'] }}
                </h3>
                <h2>
                    Liste des départements
                </h2>
                <h4 ng-repeat="item in items" ods-results="items" ods-results-context="depts">
                    {{ item.fields.dep_name }}
                </h4>
            </div>
        </div>
    </ods-dataset-context>
</div>

Map drilldown

In this example, the french administrative regions are displayed first, the user click refines on both regions and departments datasets and will then display departments in the selected region. Another click on a departement will refine departements and cities datasets and display cities in this department.

If your layer is displayed as raw or aggregation, you can configure a layer so that a click on an item triggers a refine on another context, using refineOnClickContext. One or more contexts can be defined:

<div class="row ods-box">
    <ods-dataset-context context="regions,depts,comm"
                         regions-domain="userclub"
                         regions-dataset="regions-et-collectivites-doutre-mer-france"
                         depts-domain="userclub"
                         depts-dataset="departements-et-collectivites-doutre-mer-france"
                         comm-domain="userclub"
                         comm-dataset="communes-et-arrondissements-municipaux-france">
        <div class="col-md-8">
            <ods-map style="height:560px" scroll-wheel-zoom="false">
                <ods-map-layer context="regions"
                               refine-on-click-context="[regions,depts]"
                               refine-on-click-regions-map-field="reg_code"
                               refine-on-click-regions-context-field="reg_code"
                               refine-on-click-depts-map-field="reg_code"
                               refine-on-click-depts-context-field="reg_code"
                               color="#3D3D3D"
                               show-if="!regions.parameters['refine.reg_code']">
                </ods-map-layer>
                <ods-map-layer context="depts"
                               refine-on-click-context="[depts,comm]"
                               refine-on-click-depts-map-field="dep_code"
                               refine-on-click-depts-context-field="dep_code"
                               refine-on-click-comm-map-field="dep_code"
                               refine-on-click-comm-context-field="dep_code"
                               color="#3D3D3D"
                               show-if="depts.parameters['refine.reg_code'] && !comm.parameters['refine.dep_code']">
                </ods-map-layer>
                <ods-map-layer context="comm"
                               show-if="comm.parameters['refine.dep_code']">
                </ods-map-layer>
            </ods-map>
        </div>
        <div class="col-md-4">
              <span class="ods-button" ng-click="comm.parameters = {}; depts.parameters = {}; regions.parameters = {};">
                  Supprimer tous les filtres
              </span>
            <h2>
                Jeu de données région
            </h2>
            <h4>
                <span ng-if="!regions.parameters['refine.reg_code']">Aucun filtre actif</span>
                <span ng-if="regions.parameters['refine.reg_code']">Filtre actif : reg_code = {{ regions.parameters['refine.reg_code'] }}</span>
            </h4>
            <h2>
                Jeu de données département
            </h2>
            <h4>
                <span ng-if="!(depts.parameters['refine.dep_code'] || depts.parameters['refine.reg_code'])">Aucun filtre actif</span>
                <span ng-if="depts.parameters['refine.reg_code']">Filtre actif : reg_code = {{ depts.parameters['refine.reg_code'] }}</span>
                <span ng-if="depts.parameters['refine.dep_code']"><br/>Filtre actif: dep_code = {{ depts.parameters['refine.dep_code'] }}</span>
            </h4>
            <h2>
                Jeu de données commune
            </h2>
            <h4>
                <span ng-if="!comm.parameters['refine.dep_code']">Aucun filtre actif</span>
                <span ng-if="comm.parameters['refine.dep_code']">Filtre actif : dep_code = {{ comm.parameters['refine.dep_code'] }}</span>
            </h4>
        </div>
    </ods-dataset-context>
</div>

Dynamic map by zoom level

In this example, the French administrative regions, departments and cities are displayed in a specific zoom range. Regions from the minimum to 6, Departments from 7 to 8, Cities from 9 to the maximum

You can also configure layers to only be visible between certain zoom levels, using show-zoom-min, show-zoom-max, or both.

<ods-dataset-context context="regions,depts,comm"
                     regions-domain="userclub"
                     regions-dataset="regions-et-collectivites-doutre-mer-france"
                     depts-domain="userclub"
                     depts-dataset="departements-et-collectivites-doutre-mer-france"
                     comm-domain="userclub"
                     comm-dataset="communes-et-arrondissements-municipaux-france">
    <ods-map location="5,44.73113,4.21875" style="height:560px">
        <ods-map-layer context="regions"
                       color="#3D3D3D"
                       show-zoom-max="6">
        </ods-map-layer>
        <ods-map-layer context="depts"
                       color="#3D3D3D"
                       show-zoom-min="7"
                       show-zoom-max="8">
        </ods-map-layer>
        <ods-map-layer context="comm"
                       color="#c0c0c0"
                       show-zoom-min="9">
        </ods-map-layer>
    </ods-map>
</ods-dataset-context>

Replace map tooltip with a refine : in a pop-in !

In this example, refineOnClick will be used to open a modal on top on the map. It’s a good alternative to map tooltips, specially when their is a lot of content to display. It’s also easier to get contexts and refines from the page as default tooltips are in an isolated scope. that means that actions performed in the tooltip can’t have any effect on the page. In this example, modals are simple html blocs, displayed when a refine is active.

<div class="container">
    <ods-dataset-context context="map,ctxrefine"
                         map-dataset="us-hospitals"
                         map-domain="userclub"
                         map-parameters="{'refine.state':'HI'}"
                         ctxrefine-dataset="us-hospitals"
                         ctxrefine-domain="userclub">
        <div class="map">
            <ods-map basemap="streets" style="height:500px" no-refit="true">
                <ods-map-layer context="map"
                               refine-on-click-context="ctxrefine"
                               refine-on-click-context-replace-refine="true"></ods-map-layer>
            </ods-map>
            <div class="refine"
                 ng-if="ctxrefine.parameters['geofilter.distance']"
                 ods-results="items"
                 ods-results-context="ctxrefine"
                 ods-results-max="1"
                 ng-repeat="item in items">

                <div class="pop-in-card" ng-if="item">
                    <button class="pop-in-clear-button"
                            ng-click="ctxrefine.parameters['geofilter.distance'] = undefined"
                            aria-label="Close popup window">
                        <i class="fa fa-times" aria-hidden="true"></i>
                    </button>
                    <div class="pop-in-header row">
                        <div class="col-xs-4">
                            <div class="pop-in-photo-container">
                                <img class="pop-in-photo"
                                     src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/ca/Seal_of_the_State_of_Hawaii.svg/1200px-Seal_of_the_State_of_Hawaii.svg.png">
                            </div>
                        </div>
                        <div class="col-xs-8">
                            <h2 class="pop-in-title">
                                {{ item.fields.name }}
                            </h2>
                            <h3 class="pop-in-subtitle">
                                {{ item.fields.type }}
                            </h3>
                        </div>
                    </div>
                    <dl class="row pop-in-list">
                        <dt class="col-sm-4">ADDRESS</dt>
                        <dd class="col-sm-8">{{ item.fields.address }}</dd>
                        <dt class="col-sm-4">CITY</dt>
                        <dd class="col-sm-8">{{ item.fields.city }}</dd>
                        <dt class="col-sm-4">POPULATION</dt>
                        <dd class="col-sm-8">{{ item.fields.population }}</dd>
                        <dt class="col-sm-4">WEBSITE</dt>
                        <dd class="col-sm-8">
                            <a href="{{ item.fields.source }}" target="_blank">
                                Link <i class="fa fa-external-link" aria-hidden="true"></i>
                            </a>
                        </dd>
                    </dl>
                </div>
                <div class="backdrop"
                     ng-click="ctxrefine.parameters['geofilter.distance'] = undefined"></div>
            </div>
        </div>
    </ods-dataset-context>
</div>
.map {
    position: relative;
}

.map .refine {
    z-index: 20;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}

.map .refine .refine-close:hover {
    opacity: 0.65;
}

.map .backdrop {
    z-index: 19;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.7);
}




/* Pop-in
========================================================================== */
.ods-pop-in__backdrop {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    background-color: transparent;
    z-index: 101;
    cursor: auto;
}
.pop-in-clear-button {
    font-size: 1.3em;
    color: #ababab;
    position: absolute;
    top: 15px;
    right: 15px;
    border: none;
    background: none;
    z-index: 1;
}
.pop-in-clear-button:hover {
    color: #09357a;
}
.pop-in-card {
    z-index: 102;
    height: auto;
    min-width: 300px;
    width: 700px;
    background-color: white; /* Background color of card */
    color: var(--text);
    padding: 26px;
    margin: 0 10px;
    border-radius: 4px;
    text-decoration: none;
    transition: all .2s;
    position: relative;
    display: flex;
    flex-direction: column;
}
.pop-in-header {
    margin-bottom: 20px;
}
.pop-in-photo-container {
    position: relative;
    height: 100px;
    width: 100px;
    background-color: rgba(218, 218, 218, .2);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
}
.pop-in-photo {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;
    height: 100px;
    width: auto;
    max-width: 100px;
    border-radius: 50%;
}
.pop-in-title {
    font-size: 1.5rem;
    color: #071a70;
    font-weight: normal;
    margin-top: 17px !important;
    margin-bottom: 10px;
}
.pop-in-subtitle {
    font-size: 1rem;
    margin: 4px 0;
    text-transform: uppercase;
    font-weight: normal;
    border-radius: 4px;
    margin: 0;
    display: inline-block;
}
.pop-in-list {
    margin-top: 0;
    margin-bottom: 0;
}
.pop-in-list dt {
    color: #6f6f6f;
}
@media screen and (min-width: 767px) {
    .pop-in-list dt {
        text-align: right;
    }
}
.pop-in-list dd {
    margin: 0;
}

Replace map tooltip with a refine : on the side !

In this example, refineOnClick will be used to display record details on the side of the map. It’s a good alternative to the pop-in version.

To highlight the selected point on the map, the refined context is displayed. To achieve this, the context must be refined by a field that contains an unique name or ID, and therefore this field must be set has a filter/facet on the dataset.

<div class="container">
    <ods-dataset-context context="map,ctxrefine"
                         map-dataset="us-hospitals"
                         map-domain="userclub"
                         map-parameters="{'refine.state':'HI'}"
                         ctxrefine-dataset="us-hospitals"
                         ctxrefine-domain="userclub">
        <div class="row">
            <div class="col-md-7">
                <ods-map basemap="streets" style="height:500px" no-refit="true">
                    <ods-map-layer context="map"
                                   refine-on-click-context="ctxrefine"
                                   refine-on-click-ctxrefine-replace-refine="true"
                                   refine-on-click-ctxrefine-map-field="id"
                                   refine-on-click-ctxrefine-context-field="id"></ods-map-layer>
                    <ods-map-layer context="ctxrefine"
                                   show-if="ctxrefine.parameters['refine.id']"
                                   color="green"
                                   picto-size="6"></ods-map-layer>
                </ods-map>
            </div>
            <div class="col-md-5">
                <!-- WHEN NO POINT SELECTED, DISPLAY THIS -->
                <div class="unrefined"
                     ng-if="!ctxrefine.parameters['refine.id']">
                    <div class="header">
                        <h2 class="title">
                            HAWAII Hospitals
                        </h2>
                        <h3 class="subtitle">
                            click on the map to see details
                        </h3>
                    </div>
                </div>

                <!-- WHEN A POINT IS SELECTED, DISPLAY THIS -->
                <div class="refined"
                     ng-if="ctxrefine.parameters['refine.id']"
                     ods-results="items"
                     ods-results-context="ctxrefine"
                     ods-results-max="1"
                     ng-repeat="item in items">
                    <button class="button-go-back"
                            ng-click="ctxrefine.parameters['refine.id'] = undefined"
                            aria-label="Close detail side">
                        <i class="fa fa-long-arrow-left button-go-back-icon"
                           aria-hidden="true"></i>
                        <span>back</span>
                    </button>

                    <!-- DETAILS SIDE, AS AN EXAMPLE: YOU SHOULD REMOVE THIS BLOCK AND DO YOUR OWN -->
                    <div class="pop-in-header row">
                        <div class="col-xs-4">
                            <div class="pop-in-photo-container">
                                <img class="pop-in-photo"
                                     src="https://upload.wikimedia.org/wikipedia/commons/thumb/c/ca/Seal_of_the_State_of_Hawaii.svg/1200px-Seal_of_the_State_of_Hawaii.svg.png">
                            </div>
                        </div>
                        <div class="col-xs-8">
                            <h2 class="pop-in-title">
                                {{ item.fields.name }}
                            </h2>
                            <h3 class="pop-in-subtitle">
                                {{ item.fields.type }}
                            </h3>
                        </div>
                    </div>
                    <dl class="row pop-in-list">
                        <dt class="col-sm-4">ADDRESS</dt>
                        <dd class="col-sm-8">{{ item.fields.address }}</dd>
                        <dt class="col-sm-4">CITY</dt>
                        <dd class="col-sm-8">{{ item.fields.city }}</dd>
                        <dt class="col-sm-4">POPULATION</dt>
                        <dd class="col-sm-8">{{ item.fields.population }}</dd>
                        <dt class="col-sm-4">WEBSITE</dt>
                        <dd class="col-sm-8">
                            <a href="{{ item.fields.source }}" target="_blank">
                                Link <i class="fa fa-external-link" aria-hidden="true"></i>
                            </a>
                        </dd>
                    </dl>
                    <!-- END OF DETAIL SIDE -->
                </div>
            </div>
        </div>
    </ods-dataset-context>
</div>
/* Unrefined state
========================================================================== */
.unrefined .header {
    text-align: center;
}
.unrefined .title {
    font-size: 1.5rem;
    color: #071a70;
    font-weight: normal;
    margin-top: 17px !important;
    margin-bottom: 10px;
}
.unrefined .subtitle {
    font-size: 1rem;
    margin: 4px 0;
    text-transform: uppercase;
    font-weight: normal;
    border-radius: 4px;
    margin: 0;
    display: inline-block;
}


/* Button Go Back
========================================================================== */
.button-go-back {
    font-size: 1.2rem;
    color: var(--text);
    padding: .5rem 1rem;
    border-radius: .4rem;
    border-width: 1px;
    margin: 0 0 2rem 0;
    display: inline-block;
    text-decoration: none;
    transition: all .2s;
    background-color: #D9F1FF; /* Button background color */
}
.button-go-back:hover {
    text-decoration: none;
    background-color: #A6DEFF; /* Button background color on mouse hover */
}
.button-go-back-icon {
    margin-right: .4rem;
    position: relative;
    left: 0;
    transition: all .3s;
}
.button-go-back:hover .button-go-back-icon {
    left: -.3rem;
}




/* Side detail as an exemple, you should remove this part and do you own
========================================================================== */
.pop-in-header {
    margin-bottom: 20px;
}
.pop-in-photo-container {
    position: relative;
    height: 100px;
    width: 100px;
    background-color: rgba(218, 218, 218, .2);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
}
.pop-in-photo {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;
    height: 100px;
    width: auto;
    max-width: 100px;
    border-radius: 50%;
}
.pop-in-title {
    font-size: 1.5rem;
    color: #071a70;
    font-weight: normal;
    margin-top: 17px !important;
    margin-bottom: 10px;
}
.pop-in-subtitle {
    font-size: 1rem;
    margin: 4px 0;
    text-transform: uppercase;
    font-weight: normal;
    border-radius: 4px;
    margin: 0;
    display: inline-block;
}
.pop-in-list {
    margin-top: 0;
    margin-bottom: 0;
}
.pop-in-list dt {
    color: #6f6f6f;
}
@media screen and (min-width: 767px) {
    .pop-in-list dt {
        text-align: right;
    }
}
.pop-in-list dd {
    margin: 0;
}