Add osm-map-nano

This commit is contained in:
Corentin 2025-09-10 00:22:48 +09:00
commit bedef43e13
22 changed files with 739 additions and 0 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
data/* filter=lfs diff=lfs merge=lfs -text

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
build
node_modules

2
.npmrc Normal file
View file

@ -0,0 +1,2 @@
omit[] = "optional"
package-lock = false

BIN
data/shibuya.osm (Stored with Git LFS) Normal file

Binary file not shown.

BIN
data/test.png (Stored with Git LFS) Normal file

Binary file not shown.

24
eslint.config.js Normal file
View file

@ -0,0 +1,24 @@
import { defineConfig } from 'eslint/config';
import tsparser from '@typescript-eslint/parser';
import stylistic from '@stylistic/eslint-plugin';
export default defineConfig([
{
files: ['**/*.js', '**/*.cjs', '**/*.ts'],
languageOptions: {
parser: tsparser,
sourceType: 'module',
},
ignores: ['**/dist/*', '**/*.min.js'],
plugins: {
'@stylistic': stylistic
},
rules: {
'@stylistic/array-bracket-spacing' : ['error', 'never'],
'@stylistic/arrow-spacing' : ['error', {'before': true, 'after': true}],
'@stylistic/block-spacing' : ['error', 'always'],
'@stylistic/quotes' : ['error', 'single'],
'@stylistic/semi' : ['error', 'always']
}
}
]);

34
kousen_home.osm Normal file
View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="openstreetmap-cgimap 2.1.0 (682953 spike-08.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/">
<bounds minlat="33.9336800" minlon="134.2833100" maxlat="33.9986000" maxlon="134.4180700"/>
<node id="268978937" visible="true" version="2" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9763341" lon="134.3615177"/>
<way id="1299239449" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580">
<nd ref="12035615873"/>
<nd ref="12035615874"/>
<nd ref="12035615875"/>
<nd ref="12035615876"/>
<nd ref="12035615877"/>
<nd ref="12035615878"/>
<nd ref="12035615879"/>
<nd ref="12035615880"/>
<nd ref="12035615881"/>
<nd ref="12035615882"/>
<nd ref="12035615883"/>
<nd ref="12035615884"/>
<nd ref="12035615873"/>
<tag k="building" v="school"/>
<tag k="name" v="神山まるごと高等専門学校"/>
</way>
<node id="12035615873" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9728406" lon="134.3623724"/>
<node id="12035615874" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9727562" lon="134.3623792"/>
<node id="12035615875" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9728069" lon="134.3632910"/>
<node id="12035615876" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9729026" lon="134.3632832"/>
<node id="12035615877" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9728926" lon="134.3631019"/>
<node id="12035615878" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9729577" lon="134.3630967"/>
<node id="12035615879" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9729507" lon="134.3629704"/>
<node id="12035615880" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9728868" lon="134.3629755"/>
<node id="12035615881" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9728680" lon="134.3626345"/>
<node id="12035615882" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9729233" lon="134.3626301"/>
<node id="12035615883" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9729167" lon="134.3625115"/>
<node id="12035615884" visible="true" version="1" changeset="153581872" timestamp="2024-07-05T10:24:33Z" user="awa showtaro" uid="6809580" lat="33.9728486" lon="134.3625170"/>
</osm>

28
package.json Normal file
View file

@ -0,0 +1,28 @@
{
"name": "js-osm",
"version": "0.0.1",
"description": "",
"license": "LGPL-3.0-or-later",
"author": "",
"type": "module",
"scripts": {
"prebuild": "./scripts/tmpfs_dirs.sh",
"build": "tsc --build && node scripts/build.js",
"clean": "rm -rf build/*",
"test": "node scripts/test.cjs"
},
"workspaces": [
"packages/*"
],
"devDependencies": {
"@rollup/rollup-linux-x64-gnu": "^4.50.1",
"@stylistic/eslint-plugin": "^5.3.1",
"@typescript-eslint/eslint-plugin": "^8.42.0",
"eslint": "^9.34.0",
"osm-map-nao": "./packages/osm-map-nano",
"rollup": "^4.50.1",
"selenium-webdriver": "^4.35.0",
"typescript": "^5.9.2",
"typescript-eslint": "^8.42.0"
}
}

View file

@ -0,0 +1,12 @@
'use strict';
import { Mapper } from './src/Mapper.js';
module.exports = {Mapper};
// module.exports = require('./src/index.js');
// if(process.env.NODE_ENV === 'production')
// module.exports = require('./dist/index.min.cjs');
// else
// module.exports = require('./dist/index.cjs');

View file

@ -0,0 +1,13 @@
{
"name": "osm-map-nano",
"version": "0.0.1",
"description": "",
"license": "LGPL-3.0-or-later",
"author": "",
"type": "module",
"main": "src/index.js",
"scripts": {
"lint": "eslint packages/*",
"test": "echo \"Error: no test specified\" && exit 1"
}
}

View file

@ -0,0 +1,313 @@
namespace OSM
{
export type Tags = {
[key: string]: string
};
export class Bounds {
constructor(
public max_lat: number,
public max_lon: number,
public min_lat: number,
public min_lon: number) {}
}
export class Member {
constructor(
public ref: number,
public role: string,
public type: string) {}
}
export class Node {
constructor(
public id: number,
public lat: number,
public lon: number,
public tags: Tags,
public uid: number,
public version: number,
public visible: boolean) {}
}
export class Relation {
constructor(
public id: number,
public members: Member[],
public tags: Tags,
public uid: number,
public version: number,
public visible: boolean) {}
}
export class Way {
constructor(
public id: number,
public nodes: Node[],
public tags: Tags,
public uid: number,
public version: number,
public visible: boolean) {}
}
}
interface MapperOptions {
bounds?: OSM.Bounds,
canvas?: HTMLCanvasElement
}
export class Mapper
{
current_bounds: OSM.Bounds | null;
data_bounds: OSM.Bounds | null;
nodes: Map<Number, OSM.Node> = new Map();
relations: Map<Number, OSM.Relation> = new Map();
ways: Map<Number, OSM.Way> = new Map();
canvas: HTMLCanvasElement;
zoom_level: number = 0;
zoom_scale: number = 0;
private readonly DEG2RAD: number = Math.PI / 180;
private readonly PI_4: number = Math.PI / 4;
constructor({bounds, canvas}: MapperOptions = {})
{
this.current_bounds = bounds || null;
this.data_bounds = bounds || null;
this.canvas = canvas || document.createElement('canvas');
}
private getXMLTags(element: Element): OSM.Tags
{
let tags: OSM.Tags = {};
element.querySelectorAll('tag').forEach(tag => {
tags[tag.getAttribute('k') || ''] = tag.getAttribute('v') || '';
});
return tags;
}
private getXMLVisible(element: Element): boolean
{
const visible: string = element.getAttribute('visible') || 'false';
return visible === '1' || visible.toLowerCase() === 'true';
}
public loadXMLData(xml_data: string)
{
const parser = new DOMParser();
const doc = parser.parseFromString(xml_data, 'application/xml');
const doc_bounds = doc.querySelector('bounds');
if(doc_bounds !== null)
{
this.data_bounds = new OSM.Bounds(
parseFloat(doc_bounds.getAttribute('maxlat') || '0'),
parseFloat(doc_bounds.getAttribute('maxlon') || '0'),
parseFloat(doc_bounds.getAttribute('minlat') || '0'),
parseFloat(doc_bounds.getAttribute('minlon') || '0')
);
this.current_bounds = this.data_bounds;
}
doc.querySelectorAll('node').forEach(doc_node => {
const node_id: number = parseInt(doc_node.getAttribute('id') || '0');
this.nodes.set(node_id, new OSM.Node(
node_id,
parseFloat(doc_node.getAttribute('lat') || '0'),
parseFloat(doc_node.getAttribute('lon') || '0'),
this.getXMLTags(doc_node),
parseFloat(doc_node.getAttribute('uid') || '0'),
parseFloat(doc_node.getAttribute('version') || '0'),
this.getXMLVisible(doc_node)
));
});
doc.querySelectorAll('way').forEach(doc_way => {
const way_id: number = parseInt(doc_way.getAttribute('id') || '0');
let nodes: OSM.Node[] = [];
doc_way.querySelectorAll('nd').forEach(way_nd => {
const node_id: number = parseInt(way_nd.getAttribute('ref') || '0');
const node = this.nodes.get(node_id);
if(node === undefined)
throw `Invalid data : reference to non existent Node ${node_id} from Way ${way_id}`;
nodes.push(node);
});
this.ways.set(way_id, new OSM.Way(
way_id,
nodes,
this.getXMLTags(doc_way),
parseFloat(doc_way.getAttribute('uid') || '0'),
parseFloat(doc_way.getAttribute('version') || '0'),
this.getXMLVisible(doc_way)
));
});
doc.querySelectorAll('relation').forEach(doc_relation => {
const relation_id: number = parseInt(doc_relation.getAttribute('id') || '0');
let members: OSM.Member[] = [];
doc_relation.querySelectorAll('member').forEach(member => {
members.push(new OSM.Member(
parseInt(member.getAttribute('ref') || '0'),
member.getAttribute('role') || '',
member.getAttribute('rype') || ''));
});
this.relations.set(relation_id, new OSM.Relation(
relation_id,
members,
this.getXMLTags(doc_relation),
parseFloat(doc_relation.getAttribute('uid') || '0'),
parseFloat(doc_relation.getAttribute('version') || '0'),
this.getXMLVisible(doc_relation)
));
});
}
private get_map_raw_coords(lon: number, lat: number): [number, number]
{
return [
this.zoom_scale * lon * this.DEG2RAD,
this.zoom_scale * Math.log(Math.tan(this.PI_4 + (lat * this.DEG2RAD / 2)))
];
}
public draw(target_width: number, canvas?: HTMLCanvasElement)
{
if(canvas)
this.canvas = canvas;
if(this.current_bounds === null || this.data_bounds === null)
return;
this.zoom_level = Math.ceil(-Math.log2(
((this.current_bounds.max_lon - this.current_bounds.min_lon) * 2 * Math.PI) / target_width));
this.zoom_scale = (2**this.zoom_level) * target_width / (2 * Math.PI);
const [min_x, min_y] = this.get_map_raw_coords(this.current_bounds.min_lon, this.current_bounds.min_lat);
const [max_x, max_y] = this.get_map_raw_coords(this.current_bounds.max_lon, this.current_bounds.max_lat);
const width = max_x - min_x;
const height = max_y - min_y;
this.canvas.width = width;
this.canvas.height = height;
let ctx = this.canvas.getContext('2d', {alpha: false}) as CanvasRenderingContext2D;
const get_map_coords: (lon: number, lat:number) => [number, number] = (lon: number, lat: number) => {
const [x, y] = this.get_map_raw_coords(lon, lat);
return [Math.floor(x - min_x), Math.floor(max_y - y)];
};
const background_color = 'rgb(235, 235, 235)';
ctx.fillStyle = background_color;
ctx.fillRect(0, 0, width, height);
const draw_way_polygon = (way: OSM.Way, color: string, outline: string | null = null,
width: number = 1) => {
let path_len: number = 0;
let previous_coords: [number, number] | null = null;
let path = new Path2D();
for(const node of way.nodes)
{
const coords = get_map_coords(node.lon, node.lat);
if(previous_coords === null || previous_coords[0] !== coords[0] || previous_coords[1] !== coords[1])
{
if(previous_coords === null)
path.moveTo(coords[0], coords[1]);
else
path.lineTo(coords[0], coords[1]);
path_len += 1;
previous_coords = coords;
}
}
path.closePath();
if(path_len > 2)
{
ctx.fillStyle = color;
ctx.fill(path);
if(outline !== null)
{
ctx.lineWidth = width;
ctx.stroke(path);
}
}
};
const draw_way_line = (way: OSM.Way, width: number, color: string) => {
let path_len: number = 0;
let previous_coords: [number, number] | null = null;
let path = new Path2D();
for(const node of way.nodes)
{
const coords = get_map_coords(node.lon, node.lat);
if(previous_coords === null || previous_coords[0] !== coords[0] || previous_coords[1] !== coords[1])
{
if(previous_coords === null)
path.moveTo(coords[0], coords[1]);
else
path.lineTo(coords[0], coords[1]);
path_len += 1;
previous_coords = coords;
}
}
if(path_len > 1)
{
ctx.lineWidth = width;
ctx.strokeStyle = color;
ctx.lineJoin = 'round';
ctx.stroke(path);
}
};
const bridge_outline: string = 'rgb(80, 80, 80)';
const thickest_stroke: number = Math.floor(Math.min(width, height) / 32);
this.ways.forEach(way => {
if(way.tags['building'] !== undefined)
{
draw_way_polygon(way, 'rgb(190, 180, 160)', 'rgb(120, 120, 120)');
}
});
this.ways.forEach(way => {
if(way.tags['highway'] !== undefined)
{
switch(way.tags['highway'])
{
case 'motorway':
case 'motorway_link':
draw_way_line(way, thickest_stroke, 'rgb(40, 20, 10)');
draw_way_line(way, thickest_stroke * 0.9, 'rgb(220, 160, 160)');
break;
case 'trunk':
case 'trunk_link':
draw_way_line(way, Math.floor(thickest_stroke * 0.9),
(way.tags['bridge'] !== undefined) ? bridge_outline : 'rgb(180, 120, 50)');
draw_way_line(way, Math.floor(thickest_stroke * 0.8),'rgb(240, 170, 150)');
break;
case 'primary':
case 'primary_link':
draw_way_line(way, Math.floor(thickest_stroke * 0.8),
(way.tags['bridge'] !== undefined) ? bridge_outline : 'rgb(150, 80, 60)');
draw_way_line(way, Math.floor(thickest_stroke * 0.7),'rgb(245, 215, 150)');
break;
case 'secondary':
case 'secondary_link':
draw_way_line(way, Math.floor(thickest_stroke * 0.6),
(way.tags['bridge'] !== undefined) ? bridge_outline : 'rgb(50, 40, 20)');
draw_way_line(way, Math.floor(thickest_stroke * 0.5),'rgb(252, 250, 200)');
break;
case 'tertiary':
case 'tertiary_link':
draw_way_line(way, Math.floor(thickest_stroke * 0.4),
(way.tags['bridge'] !== undefined) ? bridge_outline : 'rgb(160, 160, 160)');
draw_way_line(way, Math.floor(thickest_stroke * 0.3),'rgb(245, 245, 245)');
break;
case 'residential':
case 'unclassified':
case 'road':
draw_way_line(way, Math.floor(thickest_stroke * 0.35),
(way.tags['bridge'] !== undefined) ? bridge_outline : 'rgb(160, 160, 160)');
draw_way_line(way, Math.floor(thickest_stroke * 0.25),'rgb(250, 250, 250)');
break;
}
}
});
}
}

View file

@ -0,0 +1,5 @@
import { Mapper } from './Mapper.js';
export {
Mapper
};

View file

@ -0,0 +1,14 @@
import { Mapper } from 'osm-map-nano';
(globalThis as any).test = (canvas: HTMLCanvasElement, data: string) => {
const test = new Mapper({canvas: canvas});
test.loadXMLData(data);
console.log(JSON.stringify(test.data_bounds));
console.log(`Nodes: ${test.nodes.size}`);
console.log(`Ways: ${test.ways.size}`);
console.log(`Relations: ${test.relations.size}`);
test.draw(1600);
console.log('Draw done');
};

View file

@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.json"
}

File diff suppressed because one or more lines are too long

78
scripts/build.js Normal file
View file

@ -0,0 +1,78 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { rollup } from 'rollup';
const root_dir = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const root_info = JSON.parse(fs.readFileSync(path.join(root_dir, 'package.json')));
async function build(options)
{
let bundle;
let success = true;
try
{
bundle = await rollup(options.input);
await bundle.write(options.output);
}
catch(error)
{
success = false;
console.error(`Error building ${options}`);
console.error(error);
}
if(bundle)
{
await bundle.close();
}
return success;
}
for(const package_path of fs.globSync(root_info.workspaces))
{
const pkg_info = JSON.parse(fs.readFileSync(path.join(root_dir, package_path, 'package.json')));
const build_dir = path.join('build', 'packages', pkg_info.name);
const dist_dir = path.join(build_dir, 'dist');
// Building main package
fs.mkdirSync(dist_dir, {recursive: true});
if(!await build({
input: {
input: path.join(build_dir, 'src', 'index.js')
},
output: {
compact: true,
extend: true,
file: `${dist_dir}/${pkg_info.name}.js`,
name: 'OSM',
format: 'iife'
}}))
process.exit(1);
// Building tests
const html_test_template = fs.readFileSync(path.join(root_dir, 'scripts', 'test_template.html')).toString()
.replaceAll('{{bundle}}', `${pkg_info.name}.js`)
.replaceAll('{{data}}', '/data/shibuya.osm');
for(const test_file of fs.globSync(path.join(build_dir, 'tests', '*.js')))
{
const test_name = path.parse(test_file).name;
if(!await build({
input: {
input: test_file,
external: ['osm-map-nano'],
},
output: {
compact: true,
globals: { 'osm-map-nano': 'OSM' },
extend: true,
file: `${dist_dir}/${test_name}.js`,
name: 'OSM',
format: 'iife'
}}))
process.exit(1);
fs.writeFileSync(`${dist_dir}/${test_name}.html`,
html_test_template.replaceAll('{{test_script}}', `${test_name}.js`).replaceAll('{{test_name}}', test_name));
}
}

66
scripts/server.js Normal file
View file

@ -0,0 +1,66 @@
import fs from 'fs';
import http from 'http';
import path from 'path';
const port = 8080;
const server = http.createServer((request, response) => {
const mimeType = {
'.html': 'text/html',
'.js': 'text/javascript',
'.gz': 'application/javascript',
};
const mimeEncoding = {
'.gz': 'gzip',
};
let pathname = path.join('.', request.url);
if(!fs.existsSync(pathname))
{
// If the file is not found, return 404
response.statusCode = 404;
response.end(`File ${pathname} not found!`);
return;
}
// If is a directory, then look for index.html
if (fs.statSync(pathname).isDirectory()) {
pathname = path.join(pathname, 'index.html');
}
// Read file from file system
fs.readFile(pathname, function(error, data){
if(error)
{
response.statusCode = 500;
response.end(`Error getting the file: ${error}.`);
}
else
{
// Based on the URL path, extract the file extention. e.g. .js, .doc, ...
const extension = path.parse(pathname).ext;
// Set Content-Type
response.setHeader('Content-Type', mimeType[extension] || 'text/plain' );
// If the file is found, set Content-Encoding
if(mimeEncoding[extension])
{
response.setHeader('Content-Encoding', mimeEncoding[extension]);
}
response.end(data);
}
});
});
server.listen(port, (error) => {
if (error)
return console.log(`Server cannot listen port ${port} :`, error);
console.log(`Server is listening on port ${port}`);
});
const exit = () => {
server.close();
console.log('Server stopped');
};
process.on('SIGINT', () => {
process.stdout.write('\r \r');
exit();
});

67
scripts/test.cjs Normal file
View file

@ -0,0 +1,67 @@
const assert = require('node:assert');
const { spawn } = require('node:child_process');
const fs = require('node:fs');
const { Browser, Builder, By } = require('selenium-webdriver');
const firefox = require('selenium-webdriver/firefox');
async function test() {
const generate_env_var = process.env.OSM_MAP_GENERATE_DATA || 'false';
const generete_data = generate_env_var === '1' || generate_env_var.toLowerCase() === 'true';
const server = spawn('node', ['scripts/server.js'], {stdio: [null, null, 'inherit']}); // show stderr (stdout is read)
console.log('Creating selenium driver');
const options = new firefox.Options();
options.addArguments('--headless');
const driver = await new Builder().forBrowser(Browser.FIREFOX).setFirefoxOptions(options).build();
try
{
const max_try = 5;
let read_try = 0;
for(; read_try < max_try; read_try +=1)
{
const server_out = server.stdout.read();
if(server_out != null)
{
assert.equal('Server is listening on port 8080\n', server_out.toString(), 'Server not starting properly');
break;
}
await driver.sleep(50);
}
assert.notEqual(read_try, max_try, 'Timeout waiting for server starting');
const test_name = 'test';
console.log('Fetching test html');
await driver.get('http://localhost:8080/build/packages/osm-map-nano/dist/test.html');
assert.equal(`OSM Map Test : ${test_name}`, await driver.getTitle(), 'Wrong HTML title');
let canvas = await driver.findElement(By.id('canvas-test'));
const image_url_prefix = 'data:image/png;base64,';
const image_data = Buffer.from(
(await driver.executeScript('return arguments[0].toDataURL()', canvas)).substring(image_url_prefix.length), 'base64');
const expected_data = fs.readFileSync('data/test.png');
if(generete_data)
{
if(!expected_data.equals(image_data))
{
console.log(`Generating image for test ${test_name}`);
fs.writeFileSync('data/test.png', image_data);
}
}
else
assert.ok(expected_data.equals(image_data));
}
catch (error)
{
console.error('TEST FAILED');
console.error(error);
}
finally
{
console.log('Exiting selenium driver');
await driver.quit();
server.kill('SIGINT');
}
}
test();

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>OSM Map Test : {{test_name}}</title>
<style>
body { background: #333; color: #ddd}
</style>
<script src="{{bundle}}"></script>
</head>
<body>
<canvas id="canvas-test"></canvas>
<script src="{{test_script}}"></script>
<script>
fetch('{{data}}')
.then(response => response.text())
.then(data => { test(document.getElementById('canvas-test'), data); } );
</script>
</body>
</html>

12
scripts/tmpfs_dirs.sh Executable file
View file

@ -0,0 +1,12 @@
#! /bin/bash
CWD=$(cd -P . && pwd)
readarray -t TMPFS_DIRS < <(df -lt tmpfs --output=target | tail -n +2)
if [[ " ${TMPFS_DIRS[*]} " =~ " ${CWD}/build " ]]; then
echo "build alread a tmpfs"
else
echo "Mounting build as tmpfs"
sudo mount -m -t tmpfs tmpfs build
fi

37
tsconfig.json Normal file
View file

@ -0,0 +1,37 @@
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "build",
"composite": true,
"declaration": true,
"removeComments": false,
"rootDir": ".",
"lib": ["DOM", "ES2023"],
"module": "nodenext",
"moduleResolution": "nodenext",
"allowSyntheticDefaultImports": true,
"alwaysStrict": true,
"allowJs": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"strictBindCallApply": true,
"strictBuiltinIteratorReturn": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"useUnknownInCatchVariables": false
},
"references": [
{"path": "packages/osm-map-nano"}
]
}

1
tsconfig.tsbuildinfo Normal file

File diff suppressed because one or more lines are too long