Documentation Index
Fetch the complete documentation index at: https://mintlify.com/spatialillusions/milsymbol/llms.txt
Use this file to discover all available pages before exploring further.
OpenLayers uses a style-based architecture where map features are assigned ol.style.Style objects rather than individual marker instances. milsymbol works naturally with this model by providing a Canvas element to ol.style.Icon, which accepts an img property for any HTMLCanvasElement. The Canvas output is preferred over SVG strings here because OpenLayers renders vector layer styles onto a single rasterized canvas, and receiving a Canvas element directly avoids an extra serialization round-trip.
Using milsymbol with ol.style.Icon
The key class is ol.style.Icon, which supports both a Canvas element (img) and pixel-based anchor coordinates. Pass the symbol canvas as img, specify the image size with imgSize, and express the anchor in pixels using anchorXUnits: "pixels" / anchorYUnits: "pixels".
var symbol = new ms.Symbol("SFG*UCI---*****", { size: 30 });
var mycanvas = symbol.asCanvas();
var iconStyle = new ol.style.Style({
image: new ol.style.Icon({
img: mycanvas,
imgSize: [
Math.floor(symbol.getSize().width),
Math.floor(symbol.getSize().height)
],
anchor: [symbol.getAnchor().x, symbol.getAnchor().y],
anchorXUnits: "pixels",
anchorYUnits: "pixels"
})
});
OpenLayers also accepts a data URL string as the src option of ol.style.Icon. You can use symbol.toDataURL() instead of symbol.asCanvas() when you do not have access to the raw canvas, but passing the canvas directly is more efficient because it skips Base64 encoding.
Handling high-DPI (retina) screens
Devices with window.devicePixelRatio > 1 need symbols rendered at a higher pixel density and then scaled down so they appear sharp. Render the symbol at size * ratio and use scale: 1 / ratio on ol.style.Icon to restore the correct CSS pixel size:
var ratio = window.devicePixelRatio || 1;
var symbol = new ms.Symbol("SFG*UCI---*****", {
size: 30 * ratio // render at physical pixel size
});
var mycanvas = symbol.asCanvas(); // already at ratio× resolution
var iconStyle = new ol.style.Style({
image: new ol.style.Icon({
scale: 1 / ratio, // scale back down so CSS pixels are correct
img: mycanvas,
imgSize: [
Math.floor(symbol.getSize().width),
Math.floor(symbol.getSize().height)
],
anchor: [symbol.getAnchor().x, symbol.getAnchor().y],
anchorXUnits: "pixels",
anchorYUnits: "pixels"
})
});
Full working example: vector layer with per-feature styles
The example below is adapted from the milsymbol OpenLayers example included in the repository. It reads features from a GeoJSON source, iterates over them, creates a symbol sized to each feature’s echelon, and sets the style directly on the feature object.
var iconSize = {
"Team/Crew": 5,
Squad: 10,
Section: 15,
"Platoon/detachment": 20,
"Company/battery/troop": 25,
"Battalion/squadron": 30,
"Regiment/group": 35,
Brigade: 40,
Division: 45,
"Corps/MEF": 50,
Army: 55,
"Army Group/front": 60,
"Region/Theater": 65,
Command: 70
};
var ratio = window.devicePixelRatio || 1;
// Load GeoJSON features into a vector source
var vectorSource = new ol.source.Vector({
features: new ol.format.GeoJSON().readFeatures(situation, {
featureProjection: "EPSG:3857"
})
});
// Assign a milsymbol style to each feature
vectorSource.forEachFeature(function (f) {
var mysymbol = new ms.Symbol(f.getProperties().SIDC, {
uniqueDesignation: f.getProperties().name
});
// Ask for the echelon, then set the size and render the canvas
var mycanvas = mysymbol
.setOptions({
size: iconSize[mysymbol.getMetadata().echelon] * ratio
})
.asCanvas();
f.setStyle(
new ol.style.Style({
image: new ol.style.Icon({
scale: 1 / ratio,
anchor: [mysymbol.getAnchor().x, mysymbol.getAnchor().y],
anchorXUnits: "pixels",
anchorYUnits: "pixels",
imgSize: [
Math.floor(mysymbol.getSize().width),
Math.floor(mysymbol.getSize().height)
],
img: mycanvas
})
})
);
});
// Build the map
var rasterLayer = new ol.layer.Tile({
preload: 4,
source: new ol.source.OSM()
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
var map = new ol.Map({
layers: [rasterLayer, vectorLayer],
target: document.getElementById("map"),
view: new ol.View({
center: ol.proj.transform([16, 59], "EPSG:4326", "EPSG:3857"),
zoom: 5
})
});
Style functions and caching
The example above sets styles once at load time. If you use a dynamic style function — a function passed to ol.layer.Vector that is called on every render frame — cache the generated styles keyed on SIDC and size, because re-generating hundreds of symbols per frame will impact scroll and zoom performance.
var symbolCache = {};
function getSymbolStyle(sidc, size) {
var cacheKey = sidc + "@" + size;
if (!symbolCache[cacheKey]) {
var symbol = new ms.Symbol(sidc, { size: size });
var mycanvas = symbol.asCanvas();
symbolCache[cacheKey] = new ol.style.Style({
image: new ol.style.Icon({
img: mycanvas,
imgSize: [
Math.floor(symbol.getSize().width),
Math.floor(symbol.getSize().height)
],
anchor: [symbol.getAnchor().x, symbol.getAnchor().y],
anchorXUnits: "pixels",
anchorYUnits: "pixels"
})
});
}
return symbolCache[cacheKey];
}
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: function (feature) {
return getSymbolStyle(feature.get("SIDC"), 30);
}
});
milsymbol can generate 1000 symbols in under 20 ms, so re-creating symbols is rarely a bottleneck during initial load. Caching becomes worthwhile when the same symbols must be styled repeatedly within a single zoom or pan gesture.