Skip to Content
ExamplesModulesCustomizing tropical cyclone markers

JavaScript SDK - Customize Tropical Cyclone Markers

Customize a tropical-cyclones point layer provided by the Tropical module. The custom style will result in displaying storm category symbols and valid dates/times along the forecast track instead of basic points.

Custom tropical cyclone marker styling example

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no'/> <title>Xweather JavaScript SDK - Tropical Systems w/Custom Styles</title> <script defer src="https://cdn.aerisapi.com/sdk/js/latest/aerisweather.min.js"></script> <link rel="stylesheet" href="https://cdn.aerisapi.com/sdk/js/latest/aerisweather.css"> <style> html, body { height: 100%; margin: 0; padding: 0; width: 100%; } #map { height: 100%; width: 100%; } </style> </head> <body> <div id="map"></div> <script> window.onload = () => { const aeris = new AerisWeather('CLIENT_ID', 'CLIENT_SECRET'); const utils = aeris.utils; aeris.views().then(views => { const map = new views.InteractiveMap(document.getElementById('map'), { center: { lat: 39.0, lon: -95.5 }, zoom: 4, layers: 'alerts,radar-est' }); map.on('ready', () => { aeris.modules().then((modules) => { modules.tropical.utils().then((tropUtils) => { // utility functions for styling const getMarkerDate = (data) => { let tzName = utils.get(data, 'profile.tz', 'UTC'); let dt = luxon.DateTime.fromISO(utils.get(data,'dateTimeISO')).setZone(tzName); return dt.toFormat('h a ccc ZZZZ'); }; const getStormMarkerConfig = (data) => { const {lat} = utils.get(data, 'loc'); const type = utils.get(data, 'details.stormCat'); const isCurrent = data.isCurrent; const isForecast = data.isForecast; const flipped = lat < 0 && tropUtils.flipForSouthernLat(type); if (isCurrent || isForecast) { const catTextStyle = { value: (type === 'STY') ? 'S': type.replace(/[^\d]/g, ''), anchor: 'start', position: 'center', translate: { y: -2 }, size: 14, color: '#ffffff', autosize: false }; const nameTextStyle = { value: getMarkerDate(data), anchor: 'start', translate: { y: 22 }, size: 11, color: '#222222', stroke: { color: '#ffffff', width: 2 }, autosize: false } let styles = { className: 'marker-tropicalcyclone', svg: { image: { url: (flipped && type === 'TD') ? tropUtils.icon(`${type}-flipped`): tropUtils.icon(type), size: [25, 25], transform: (flipped && type !== 'TD') ? 'scale(-1, 1) translate(-80, 0)': '' }, text: [catTextStyle, nameTextStyle] }, size: [80, 60], zIndex: 50 }; if (isCurrent) { styles = utils.extend({}, styles, { svg: { image: { size: [34, 34] }, text: [ utils.extend({}, catTextStyle, { translate: { y: -3 }, size: 20 }), utils.extend({}, catTextStyle, { value: utils.get(data, 'details.stormShortName'), size: 16, color: '#222222', translate: { y: 22 }, stroke: { color: '#ffffff', width: 3 } }) ] }, size: [80, 80] }); } return styles; } return { className: 'marker-tropicalcyclone', svg: { shape: { type: 'circle', fill: { color: tropUtils.color(type) }, stroke: { color: '#ffffff', width: 2 }, size: [14, 14] }, text: data.label ? { value: utils.get(data, 'details.stormShortName'), anchor: 'start', translate: { y: 12 }, autosize: false }: null }, size: data.label ? [60, 40]: [14, 14], zIndex: data.label ? 50: null }; }; map.addModule(modules.tropical.Breakpoints); map.addModule(modules.tropical.Systems, { style: { marker: (data) => getStormMarkerConfig(data, aeris.utils), polyline: { stroke: { color: '#4685D0', width: 3, dashArray: '10 7 10 7' } }, polygon: { fill: { color: '#ffffff', opacity: 0 }, stroke: { color: '#666666', width: 2, opacity: 0.7 } }, } }); }); }); }); }); }; </script> <script src="https://cdn.jsdelivr.net/npm/luxon@1.24.1/build/global/luxon.min.js"></script> </body> </html>
© 2026 Xweather (opens in a new tab)Terms of Service (opens in a new tab)Privacy Policy (opens in a new tab)