npm package: @odraw/symbols
Description: Comprehensive orienteering map symbol library supporting ISOM, ISSkiOM, ISMTBOM, and Course Design standards
Author: ODraw
Latest version: 1.0.6

# @odraw/symbols

> Comprehensive orienteering map symbol library for JavaScript/TypeScript

A complete, type-safe library of orienteering map symbols supporting ISOM, ISSkiOM, ISMTBOM, ISSprOM, ISSOM, and Course Design standards. Perfect for building orienteering mapping tools, course planning applications, and map visualization software.

## Features

- ✅ **Complete Symbol Coverage** - All ISOM 2017-2 symbols plus specialized standards
- ✅ **Multiple Standards** - ISOM, ISSkiOM, ISMTBOM, ISSprOM, ISSOM, Course Design
- ✅ **Multiple Scales** - Support for 1:4000, 1:5000, 1:7500, 1:10000, 1:12500, 1:15000, 1:20000
- ✅ **Actual SVG Geometry** - Extracts real symbol shapes from .omap files
- ✅ **Individual SVG Files** - Each symbol exported as standalone SVG with metadata
- ✅ **Type-Safe** - Full TypeScript support with comprehensive type definitions
- ✅ **Smart Categorization** - Symbols organized by category (Landforms, Water, Vegetation, etc.)
- ✅ **Flexible Filtering** - Filter by standard, category, scale, code, or search text
- ✅ **Rich Metadata** - Includes code, name, description, colors, and more
- ✅ **Zero Dependencies** - Lightweight and fast (runtime has no dependencies)
- ✅ **Tree-Shakeable** - Only import what you need

## Installation

```bash
npm install @odraw/symbols
```

## Quick Start

```typescript
import symbols from '@odraw/symbols';

// Get all symbols
const allSymbols = symbols.getAllSymbols();

// Get symbols by standard
const isomSymbols = symbols.getSymbolsByStandard('ISOM');

// Get symbols by category
const courseSymbols = symbols.getSymbolsByCategory('course');

// Get a specific symbol
const startSymbol = symbols.getSymbol('701', 'ISOM', 15000);

// Search symbols
const results = symbols.search('control');
```

## Usage Examples

### 1. Load Symbols by Standard

```typescript
import { getSymbolsByStandard, getAvailableStandards } from '@odraw/symbols';

// Get all available standards
const standards = getAvailableStandards();
// ['ISOM', 'ISSkiOM', 'ISMTBOM', 'ISSprOM', 'ISSOM', 'CourseDesign']

// Get ISOM symbols
const isomSymbols = getSymbolsByStandard('ISOM');

// Get ISSkiOM symbols
const skiSymbols = getSymbolsByStandard('ISSkiOM');
```

### 2. Browse Symbols by Category

```typescript
import { getSymbolsGroupedByCategory, getAllCategories } from '@odraw/symbols';

// Get all categories with info
const categories = getAllCategories();
// Returns: [{ id, name, description, codeRange, color, icon }, ...]

// Get symbols grouped by category
const grouped = getSymbolsGroupedByCategory({ standard: 'ISOM', scale: 15000 });

grouped.forEach((symbols, category) => {
  console.log(`${category}: ${symbols.length} symbols`);
  symbols.forEach(symbol => {
    console.log(`  ${symbol.code} - ${symbol.name}`);
  });
});
```

### 3. Display Symbol Information

```typescript
import { getSymbolsByCode } from '@odraw/symbols';

// Get all variations of symbol 701 (Start)
const startSymbols = getSymbolsByCode('701');

startSymbols.forEach(symbol => {
  console.log(`Code: ${symbol.code}`);
  console.log(`Name: ${symbol.name}`);
  console.log(`Description: ${symbol.description}`);
  console.log(`Standard: ${symbol.standard}`);
  console.log(`Scale: 1:${symbol.scale}`);
  console.log(`Category: ${symbol.category}`);
  console.log(`Color: ${symbol.color.hex}`);
  console.log(`SVG: ${symbol.svg ? 'Available' : 'Not available'}`);
});
```

### 4. Use SVG Symbols

```typescript
import { getSymbol } from '@odraw/symbols';

// Get a symbol with SVG geometry
const controlSymbol = getSymbol('702', 'CourseDesign', 10000);

if (controlSymbol?.svg) {
  // Use the SVG directly in your HTML
  document.getElementById('symbol-container').innerHTML = controlSymbol.svg;
  
  // Or create an image element
  const img = document.createElement('img');
  img.src = `data:image/svg+xml;base64,${btoa(controlSymbol.svg)}`;
  
  // SVG includes metadata as data attributes
  console.log('Symbol metadata embedded in SVG');
}

// Load individual SVG files (if exported)
const svgUrl = `./symbols/svg/CourseDesign_10000_702.svg`;
const svgContent = await fetch(svgUrl).then(r => r.text());
```

### 5. Filter Symbols

```typescript
import { filterSymbols } from '@odraw/symbols';

// Complex filtering
const filtered = filterSymbols({
  standard: ['ISOM', 'ISSprOM'],
  category: ['course', 'landforms'],
  scale: [15000, 10000],
  search: 'control'
});

// Filter by code prefix
const landformSymbols = filterSymbols({
  standard: 'ISOM',
  code: ['101', '102', '103', '104', '105']
});
```

### 6. Build a Symbol Picker

```typescript
import { 
  getSymbolsGroupedByCategory, 
  getCategoryInfo,
  type Symbol 
} from '@odraw/symbols';

function SymbolPicker({ standard = 'ISOM', scale = 15000 }) {
  const grouped = getSymbolsGroupedByCategory({ standard, scale });
  
  return (
    <div className="symbol-picker">
      {Array.from(grouped.entries()).map(([category, symbols]) => {
        const info = getCategoryInfo(category);
        
        return (
          <div key={category} className="category">
            <h3 style={{ color: info.color }}>
              {info.icon} {info.name}
            </h3>
            <div className="symbols">
              {symbols.map(symbol => (
                <div 
                  key={symbol.id}
                  className="symbol"
                  title={`${symbol.code}: ${symbol.name}\n${symbol.description}`}
                  onClick={() => onSymbolSelect(symbol)}
                >
                  <div className="icon">
                    {symbol.svg ? (
                      <div 
                        dangerouslySetInnerHTML={{ __html: symbol.svg }}
                        style={{ width: '24px', height: '24px' }}
                      />
                    ) : (
                      <div 
                        className="placeholder"
                        style={{ backgroundColor: symbol.color.hex }}
                      >
                        {symbol.code}
                      </div>
                    )}
                  </div>
                  <span>{symbol.name}</span>
                </div>
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}
```

### 7. Search Functionality

```typescript
import { search } from '@odraw/symbols';

// Search in name, description, and code
const results = search('boulder');

results.forEach(symbol => {
  console.log(`${symbol.code} - ${symbol.name} (${symbol.standard})`);
});
```

### 8. Work with Standard/Scale Combinations

```typescript
import { 
  getAvailableStandardScaleCombinations,
  getFormattedStandardScaleCombinations,
  getSymbolSetsByStandard,
  getSymbolSet
} from '@odraw/symbols';

// Get all available standard/scale combinations
const combinations = getAvailableStandardScaleCombinations();
combinations.forEach(combo => {
  console.log(`${combo.name} - ${combo.symbolCount} symbols`);
});

// Get formatted combinations for UI dropdowns
const formatted = getFormattedStandardScaleCombinations();
formatted.forEach(option => {
  console.log(`<option value="${option.value}">${option.label}</option>`);
});

// Get all symbol sets for a specific standard
const isomSets = getSymbolSetsByStandard('ISOM');
console.log(`ISOM available at ${isomSets.length} scales`);

// Get a specific symbol set
const symbolSet = getSymbolSet('ISOM', 15000);
if (symbolSet) {
  console.log(`${symbolSet.name}: ${symbolSet.symbols.length} symbols`);
}
```

### 9. Get Statistics

```typescript
import { getStatistics } from '@odraw/symbols';

const stats = getStatistics();

console.log(`Total Symbols: ${stats.totalSymbols}`);
console.log(`Symbol Sets: ${stats.totalSymbolSets}`);
console.log(`Standards: ${stats.standards.join(', ')}`);
console.log(`Scales: ${stats.scales.map(s => `1:${s}`).join(', ')}`);

stats.categories.forEach(cat => {
  console.log(`${cat.name}: ${cat.count} symbols`);
});
```

## API Reference

### Main Functions

- `getAllSymbols()` - Get all symbols
- `getSymbolById(id)` - Get symbol by unique ID
- `getSymbolsByCode(code)` - Get all symbols with a specific code
- `getSymbol(code, standard, scale)` - Get a specific symbol
- `filterSymbols(filter)` - Filter symbols by criteria
- `getSymbolsByStandard(standard)` - Get symbols by standard
- `getSymbolsByCategory(category)` - Get symbols by category
- `getSymbolsByScale(scale)` - Get symbols by scale
- `getSymbolsGroupedByCategory(filter?)` - Get symbols grouped by category
- `getSymbolsGroupedByCodePrefix(filter?)` - Get symbols grouped by code prefix
- `search(query)` - Search symbols
- `getStatistics()` - Get library statistics

### Types

```typescript
type SymbolStandard = 'ISOM' | 'ISSkiOM' | 'ISMTBOM' | 'ISSprOM' | 'ISSOM' | 'CourseDesign';

type SymbolCategory = 
  | 'landforms'
  | 'rock-boulders'
  | 'water'
  | 'vegetation-white'
  | 'vegetation-green'
  | 'vegetation-boundaries'
  | 'man-made'
  | 'technical'
  | 'course';

interface Symbol {
  id: string;
  code: string;
  name: string;
  description: string;
  standard: SymbolStandard;
  category: SymbolCategory;
  type: 'point' | 'line' | 'area' | 'text' | 'combined';
  color: MapColor;
  scale: number;
  // ... more properties
}
```

## Symbol Categories

| Category | Code Range | Description | Color |
|----------|------------|-------------|-------|
| Landforms | 101-117 | Contours, cliffs, earth banks | Brown |
| Rock & Boulders | 201-213 | Cliffs, boulders, rocky areas | Black |
| Water | 301-320 | Lakes, streams, marshes | Blue |
| Vegetation (Open) | 401-419 | Open land, cultivation | Yellow |
| Vegetation (Forest) | 501-524 | Forest, undergrowth | Green |
| Vegetation Boundaries | 525-529 | Vegetation edges | Dark Green |
| Man-made | 501-529 | Roads, buildings, fences | Black |
| Technical | 801-899 | North lines, scales | Gray |
| Course | 701-799 | Start, controls, finish | Purple |

## Supported Standards

- **ISOM 2017-2** - International Specification for Orienteering Maps
- **ISSkiOM 2019** - International Specification for Ski Orienteering Maps
- **ISMTBOM** - International Specification for Mountain Bike Orienteering Maps
- **ISSprOM 2019** - International Specification for Sprint Orienteering Maps
- **ISSOM** - International Specification for Sport Orienteering Maps
- **Course Design** - IOF Course Setting symbols

## Supported Scales

- 1:4000
- 1:5000
- 1:7500
- 1:10000
- 1:12500
- 1:15000
- 1:20000

## SVG Rendering Status

The library extracts actual symbol geometry from .omap XML files and converts them to SVG format.

### Currently Supported
- ✅ Point symbols (circles, basic shapes)
- ✅ Line symbols with proper colors and widths
- ✅ Area symbols with fills
- ✅ Text symbols with icon text
- ✅ Basic coordinate parsing and scaling
- ✅ Color extraction from .omap definitions

### Complex Features (Simplified)
- ⚠️ Multi-element symbols with start/mid/end patterns
- ⚠️ Nested symbol definitions
- ⚠️ Symbol patterns and advanced fills
- ⚠️ Rotatable symbols with proper orientation
- ⚠️ Dash patterns and line decorations
- ⚠️ Curved paths and bezier curves

### Recommendations
- **For most use cases**: The automated SVG extraction works well
- **For critical symbols**: Consider manually exporting from OpenOrienteering Mapper
- **For production**: Use the hybrid approach (automated + manual for key symbols)

## SVG Symbol Extraction

The library now automatically extracts SVG geometry from .omap files and exports individual SVG files for each symbol. This provides actual symbol renderings instead of placeholder icons.

### Automated SVG Export

Export all 3,016+ symbols as individual SVG files:

```bash
# Export all symbols to SVG files
npm run export:svg

# This creates files like:
# symbols/svg/ISOM_15000_101.svg
# symbols/svg/CourseDesign_10000_702.svg
# etc.
```

Each SVG file includes:
- Actual symbol geometry extracted from .omap files
- Complete metadata as data attributes
- Accessibility features (title, description)
- Proper scaling and viewBox

### SVG Export Options

**Option 1: Automated Export (Current)**
- ✅ Fast - exports all symbols in seconds
- ✅ Includes complete metadata
- ✅ Reproducible and automated
- ⚠️ Simplified rendering for complex symbols

**Option 2: Manual Export from OpenOrienteering Mapper**
- ✅ Most accurate rendering
- ✅ Handles all symbol complexities
- ❌ Very time-consuming (3,016+ symbols)
- ❌ Manual process

**Option 3: Hybrid Approach (Recommended)**
- Use automated export for all symbols
- Manually export critical symbols from Mapper
- Replace auto-generated with manual for important symbols

### Using Exported SVGs

```typescript
// SVG files include metadata
const svgContent = await fetch('./symbols/svg/ISOM_15000_101.svg').then(r => r.text());

// Metadata is embedded in the SVG element:
// <svg data-code="101" data-name="Contour" data-standard="ISOM" ...>
```

## Development

```bash
# Install dependencies
npm install

# Parse symbol files and extract SVG
npm run build:symbols

# Export symbols as individual SVG files
npm run export:svg

# Build library
npm run build

# Run tests
npm test

# Development mode
npm run dev
```

### SVG Export Scripts

The library includes several scripts for working with SVG symbols:

- `scripts/export-symbols-svg.ts` - Automated SVG export from .omap files
- `scripts/export-svg-guide.md` - Guide for different SVG export approaches
- `scripts/mapper-export-guide.md` - Manual export using OpenOrienteering Mapper

For detailed information about SVG extraction methods, see the guides in the `scripts/` directory.

## License

MIT

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Credits

Symbol definitions are based on the International Orienteering Federation (IOF) specifications and OpenOrienteering Mapper project.