1

I'm trying to highlight the current marker. Updating the size or color of the marker when is clicked.

Example: https://jsfiddle.net/jodarove/e0mwk4qp This example has an array of positions (lat, long)

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.css"  data-require="leaflet@0.7.3" data-semver="0.7.3" />
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div id="map"></div>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.3/leaflet.js" data-require="leaflet@0.7.3" data-semver="0.7.3"></script>
    <script type="text/javascript" src="script.js"></script>
  </body>
</html>
    // Default map
    var map = L.map('map', {
        'center': [0, 0],
        'zoom': 0,
        'layers': [
            L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                'attribution': 'Map data &copy; OpenStreetMap contributors'
            })
        ]
    });

    // Custom icon class without iconUrl
    var customIcon = L.Icon.extend({
        options: {
            shadowUrl: 'https://leafletjs.com/examples/custom-icons/leaf-shadow.png',
            iconSize:     [38, 95],
            shadowSize:   [50, 64],
            iconAnchor:   [22, 94],
            shadowAnchor: [4, 62],
            popupAnchor:  [-3, -76]
        }
    });

    // Some positions for creating markers
    var positions = [
        [0, 120],
        [0, 60],
        [0, 0],
        [0, -60],
        [0, -120]
    ];

    // Function for getting new default icon
    function getDefaultIcon () {
        return new customIcon({
            iconUrl: 'https://leafletjs.com/examples/custom-icons/leaf-green.png'
        });
    }

    // Function for getting new highlight icon
    function getHighlightIcon () {
        return new customIcon({
            iconUrl: 'https://leafletjs.com/examples/custom-icons/leaf-red.png'
        });
    }

    // Variable to keep track of highlighted marker
    var highlight = null;

    // Function for removing highlight 
    function removeHighlight () {
        // Check for highlight
        if (highlight !== null) {
            // Set default icon
            highlight.setIcon(getDefaultIcon());
            // Unset highlight
            highlight = null;
          }
    }

    // Loop over positions
    positions.forEach(function (position) {

        // Create new marker
        var marker = L.marker(position, {
            // Set default icon
            icon: getDefaultIcon()
        })

        // Marker click
        marker.on('click', function () {
            // Remove highlight
            removeHighlight();
            // Set highlight icon
            marker.setIcon(getHighlightIcon());
            // Assign highlight
            highlight = marker;
        });

        // Add marker to map;
        marker.addTo(map);

    });

    // Add map click handler, remove highlight
    map.on('click', removeHighlight);

On my the map I'm pulling data from carto. I'm not sure how can I create this array with the positions of the markers? or there is a better way to achieve this?

Here is my code: https://jsfiddle.net/jodarove/7dork34a/

html
<!DOCTYPE html>
<html>

  <head>
    <title>Attacks on Ukraine Agriculture and Food Supply Chain | Global Food Security Program</title>
    <meta name="viewport" content="initial-scale=1.0" />
    <meta charset="utf-8" />
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600" rel="stylesheet" />
    <link href="https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300,700" rel="stylesheet" />

    <!-- Share meta -->
    <meta property="og:locale" content="en_US" />
    <meta property="og:type" content="article" />
    <meta property="og:title" content="Guyanese Diaspora Worldwide - CSIS Americas Program" />

    <script src="https://unpkg.com/babel-polyfill@6.26.0/dist/polyfill.js"></script>

    <!-- Include new Leaflet CSS -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
    <!-- Include new Leaflet CSS -->
    <!-- Make sure you put this AFTER Leaflet's CSS -->
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>

    <!-- Include CARTO.js -->
    <script src="https://libs.cartocdn.com/carto.js/v4.1.11/carto.min.js"></script>
    <link href="https://carto.com/developers/carto-js/examples/maps/public/style.css" rel="stylesheet" />

    <link rel="preconnect stylesheet" href="https://use.typekit.net/ats0hoc.css" />

    <link href="css/map.css" rel="stylesheet" />

    <!-- Google font -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Spectral:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
  </head>

  <body>
    <div class="containerall">
      <div id="map" class="map" tabindex="0"></div>

      <section class="toolbox">
        <header>
          <h1 class="box-header"><span>Attacks on Ukraine Agriculture and Food Supply Chain</span></h1>
        </header>
        <p class="description">
          Click to learn more.
        </p>
      </section>

      <section class="panel">
        <button class="close-btn">&times;</button>
        <div class="panel-content"></div>
      </section>
      <footer class="js-footer"></footer>
    </div>
    <script src="js/map.js"></script>
  </body>

</html>
var basemap = L.tileLayer(
  "https://api.mapbox.com/styles/v1/ilabmedia/cl4r8xsu6000215p70wd971j7/tiles/256/{z}/{x}/{y}@2x?access_token=pk.eyJ1IjoiaWxhYm1lZGlhIiwiYSI6ImNpbHYycXZ2bTAxajZ1c2tzdWU1b3gydnYifQ.AHxl8pPZsjsqoz95-604nw",
  {}
);

var map = L.map("map", {
  center: [48.2, 32.6],
  zoom: 6,
  maxZoom: 8,
  scrollWheelZoom: true,
  minZoom: 2,
  zoomControl: true,
  scrollWheelZoom: true,
  layers: [basemap],
  attributionControl: false,
});

const client = new carto.Client({
  apiKey: "",
  username: "",
});

const mapSource = new carto.source.SQL(`SELECT * FROM edodd_attacks_on_ukraine_agriculture_food_supply_chain_1`);

const mapStyle = new carto.style.CartoCSS(`
#layer {
  marker-width: 20;
  marker-fill: ramp([marker_color], (#482d9e, #e32c31, #005e38, #376dc2, #3cc954, #444444, #cc1b15, #ffcc00), ("#482d9e", "#e32c31", "#005e38", "#376dc2", "#3cc954", "#444444", "#cc1b15", "#ffcc00"), "=");
  marker-fill-opacity: 0.75;
  marker-file: ramp([marker_color], (url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg'), url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg'), url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg'), url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg'), url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg'), url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg'), url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg'), url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg')), ("#482d9e", "#e32c31", "#005e38", "#376dc2", "#3cc954", "#444444", "#cc1b15", "#ffcc00"), "=");
  marker-allow-overlap: true;
  marker-line-width: 0;
  marker-line-color: #FFFFFF;
  marker-line-opacity: 1;
}
`);

const mapLayer = new carto.layer.Layer(mapSource, mapStyle, {
  featureOverColumns: [
    "cartodb_id",
    "title",
    "description",
    "url",
    "media_url",
    "marker_color",
    "tweethtml"
  ],
});

client.addLayer(mapLayer);

client.getLeafletLayer().bringToFront().addTo(map);

const sidePanel = L.popup({ closeButton: true });

mapLayer.on(carto.layer.events.FEATURE_CLICKED, createSidePanel);

mapLayer.on('featureClicked', featureEvent => {
  const content = `
    <h3>${featureEvent.data.title.toUpperCase()}</h3>
    <p class="open-sans">${featureEvent.data.description} <small>max inhabitants</small></p>
  `;

  document.getElementById('info').innerHTML = content;
});

function createSidePanel(event) {
  sidePanel.setLatLng(event.latLng);
  const panel = document.querySelector('.panel');
  const panelContent = document.querySelector('.panel-content');
  panel.classList.add('open');

  if (!sidePanel.isOpen()) {
    var data = event.data;
    var content = '';

    content += `
    <h2 class="sidePanelHeaderStyle">
      ${data.title}
    </h2>
    <p class="side-panel-value"><span>${data.description}</span></p>
    `
    if (data.tweethtml) {
      content += `<div class="tweetContainer">${data.tweethtml}</div>`
    }
    if (data.url) {
      content +=
      `
      <p class="side-panel-link">For more details, click <a href="${data.url}" target="_blank">here</a>.</p>
      `;
    }
    panelContent.innerHTML = content;
  }
}

const closeBtn = document.querySelector('.close-btn');
closeBtn.addEventListener('click', function(e) {
  const panel = document.querySelector('.panel');
  panel.classList.remove('open');
})

L.control
  .attribution({
    position: "bottomright",
  })
  .setPrefix(
    '<a href="https://www.csis.org/programs/global-food-security-program">CSIS Global Food Security Program</a>, <a href="https://leafletjs.com/">Leaflet</a>'
  )
  .addTo(map);

enter image description here

0

1 Answer 1

1

I'm not familiar with Carto maps or Leaflet cartodb-leaflet plugin, so answer may not be optimal, but it works.

To display selected marker with different style, carto.filter.Category source filter can be used. First on original layer so that selected marker is left out, and then on additionally created layer where only selected marker is shown with different style.

To change selected marker when marker is shown, filter.set method is used to change filter value, using selected marker cartodb_id value.

Relevant part of the code could then look something like this:

var selectedMarkerId = -1;

var sql = 'SELECT * FROM edodd_attacks_on_ukraine_agriculture_food_supply_chain_1';

const mapSource = new carto.source.SQL(sql);   
const filter = new carto.filter.Category('cartodb_id', { notEq: selectedMarkerId });
mapSource.addFilter(filter);

const mapSource2 = new carto.source.SQL(sql);
const filter2 = new carto.filter.Category('cartodb_id', { eq: selectedMarkerId });
mapSource2.addFilter(filter2);

const mapStyle = new carto.style.CartoCSS(`
#layer {
  marker-width: 20;
  marker-fill: ramp([marker_color], (#482d9e, #e32c31, #005e38, #376dc2, #3cc954, #444444, #cc1b15, #ffcc00), ("#482d9e", "#e32c31", "#005e38", "#376dc2", "#3cc954", "#444444", "#cc1b15", "#ffcc00"), "=");
  marker-fill-opacity: 0.75;
  marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg');
  marker-allow-overlap: true;
  marker-line-width: 0;
  marker-line-color: #FFFFFF;
  marker-line-opacity: 1;
}
`);
const mapStyle2 = new carto.style.CartoCSS(`
#layer {
  marker-width: 40;
  marker-fill: ramp([marker_color], (#482d9e, #e32c31, #005e38, #376dc2, #3cc954, #444444, #cc1b15, #ffcc00), ("#482d9e", "#e32c31", "#005e38", "#376dc2", "#3cc954", "#444444", "#cc1b15", "#ffcc00"), "=");
  marker-fill-opacity: 1;
  marker-file: url('https://s3.amazonaws.com/com.cartodb.users-assets.production/production/csis/assets/20220622193635location-pin.svg');
  marker-allow-overlap: true;
  marker-line-width: 0;
  marker-line-color: #FFFFFF;
  marker-line-opacity: 1;
}
`);

const mapLayer = new carto.layer.Layer(mapSource, mapStyle, {
  featureOverColumns: [
    "cartodb_id",
    "title",
    "description",
    "url",
    "media_url",
    "marker_color",
    "tweethtml"
  ],
});
const mapLayer2 = new carto.layer.Layer(mapSource2, mapStyle2, {
  featureOverColumns: [
    "cartodb_id",
    "title",
    "description",
    "url",
    "media_url",
    "marker_color",
    "tweethtml"
  ],
});

client.addLayer(mapLayer);
client.addLayer(mapLayer2);

var leafletLayer = client.getLeafletLayer();
leafletLayer.bringToFront().addTo(map);

const sidePanel = L.popup({ closeButton: true });

mapLayer.on(carto.layer.events.FEATURE_CLICKED, createSidePanel);

function showSelectedMarker(id) {
  selectedMarkerId = id;
  filter.set('notEq', selectedMarkerId);
  filter2.set('eq', selectedMarkerId);
}

mapLayer.on('featureClicked', featureEvent => {
  if (selectedMarkerId != featureEvent.data.cartodb_id) {
    showSelectedMarker(featureEvent.data.cartodb_id);
  }
  const content = `
    <h3>${featureEvent.data.title.toUpperCase()}</h3>
    <p class="open-sans">${featureEvent.data.description} <small>max inhabitants</small></p>
  `;
});

const closeBtn = document.querySelector('.close-btn');
closeBtn.addEventListener('click', function(e) {
  const panel = document.querySelector('.panel');
  panel.classList.remove('open');
  showSelectedMarker(-1);
})

Since Carto markers are created server side as raster tiles, changing style means reload of the whole marker layer, which results in markers blink. I'm not aware of any way to avoid this.

1
  • Thank you! I really appreciate your explanation!
    – jdromero88
    Jul 5 at 13:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.