TIC

We provide advanced but simple to use vector maps with styles for displaying rich data about properties, addresses, roads and environment and many other things. You can use Mapbox or MapLibre GL JS to render the maps in both web and mobile apps.

Maps are updated daily and hot swapped.

Before Getting Started

The tile server only serves pages whose origin your team has registered. Register every origin that shows the maps: scheme, host and the port when it is not the default, without a path. While developing you can register your local origin too, e.g. http://localhost:5173.

cURLbash
curl -X POST "https://lens-api.tic.io/team/tiles/cors" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "https://maps.example.com" }'
Responsejson
{
  "teamCorsId": 42,
  "domain": "https://maps.example.com",
  "lastUpdatedAtUtc": "2026-10-07T12:00:00Z"
}

The origin works right away. Registering the same origin twice answers 409, and a value with a path or query answers 400.

List the team’s origins, and remove one by its teamCorsId:

cURLbash
curl -H "x-api-key: YOUR_API_KEY" "https://lens-api.tic.io/team/tiles/cors"

curl -X DELETE -H "x-api-key: YOUR_API_KEY" "https://lens-api.tic.io/team/tiles/cors/42"

Getting Started

This example shows how you get started with MapLibre GL JS in a webpage.

Include the JavaScript and CSS files in the head of your HTML file.

<script src='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js'></script>
<link href='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css' rel='stylesheet' />

Include the following code in the body of your HTML file.

<div id='map' style='width: 800px; height: 800px;'></div>

<script>
  var map = new maplibregl.Map({
      container: "map",
      style: "https://tiles2.tic.io/style/super-map-style2",
      center: [19.8756, 63.5569], // starting position [lng, lat]
      zoom: 4,  // starting zoom
      attributionControl: false
  });
</script>

Draw additional details

Property borders and buildings are by default included in the style but if you wish to draw areas and greater details about building numbers, usage type etc you can download the property details from the property summary (GET https://lens-api.tic.io/properties/se/{uuid}/summary). It gives you additional data and areas.

In the example below we are adding four(4) layers:

  • Areas
  • Building parts (already included in the default style)
  • Building parts text (number)
  • Areas text (number)

Result will look like the example below where the area is filled with light blue opacity and buildings are drawn in yellow adding the text number.

Vector map with areas and building numbers

For simplicity we are using Lodash.

Include the JavaScript and CSS files in the head of your HTML file.

<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script src='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js'></script>
<link href='https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css' rel='stylesheet' />
<div id='map' style='width: 800px; height: 800px;'></div>

<script>
  // See above: in your server controller / backend, request
  // https://lens-api.tic.io/properties/se/{uuid}/summary (replace uuid with the property id)
  // with your API key in the x-api-key header, and pass the response to the page
  const dataFromRequest = {};
  // Then copy and paste the code below

  const omraden = dataFromRequest.omraden;
  const byggnader = dataFromRequest.byggnader;

  var map = new maplibregl.Map({
      container: "map",
      style: "https://tiles2.tic.io/style/super-map-style2",
      center: [19.8756, 63.5569], // starting position [lng, lat]
      zoom: 4,  // starting zoom
      attributionControl: false
  });

  let bounds = [];
  map.on('load', async () => {
      const coordinates = [];
      const geojsonOmraden = {
          type: 'geojson',
          data: {
              "type": 'FeatureCollection',
              "features": []
          }
      };
      _.forEach(omraden, function(omrade) {
          geojsonOmraden.data.features.push({
              type: 'Feature',
              properties: {
                  omradesnummer: omrade.omradesnummer
              },
              geometry: {
                  type: 'Polygon',
                  coordinates: omrade.omradeYta
              }
          });
          _.forEach(omrade.omradeYta[0], i => coordinates.push(i));
      });
      map.addSource('omraden', geojsonOmraden);
      map.addLayer({
          'id': 'omraden',
          'type': 'fill',
          'source': 'omraden',
          'paint': {
              'fill-color': '#90E0EF',
              'fill-opacity': 0.7
          },
          'filter': ['==', '$type', 'Polygon']
      });

      const geojsonByggnadsdelar = {
          type: 'geojson',
          data: {
              "type": 'FeatureCollection',
              "features": []
          }
      };

      _.forEach(byggnader, function(byggnad) {
          _.forEach(byggnad.delar, function(del) {
              geojsonByggnadsdelar.data.features.push({
                  type: 'Feature',
                  properties: {
                      byggnadHusnummer: byggnad.byggnadHusnummer
                  },
                  geometry: {
                      type: 'Polygon',
                      coordinates: [del.byggnadsdelYta]
                  }
              });
          });
      });

      map.addSource('byggnadsdelar', geojsonByggnadsdelar);
      map.addLayer({
          'id': 'byggnadsdelar',
          'type': 'fill',
          'source': 'byggnadsdelar',
          'paint': {
              'fill-color': '#FDC86C',
              'fill-opacity': 0.8,
              'fill-outline-color': '#F9A107'
          },
          'filter': ['==', '$type', 'Polygon']
      });

      map.addLayer({
          'id': 'byggnadsdelartext',
          'type': 'symbol',
          'source': 'byggnadsdelar',
          "layout": {
              "text-field": [
                  "concat",
                  "Husnr: ",
                  ["get", "byggnadHusnummer"],
              ],
              "text-font": ["Open Sans SemiBold"],
              "text-size": {
                  "stops": [
                      [2, 10],
                      [4, 12],
                      [6, 12],
                      [12, 12],
                      [16, 16]
                  ]
              },
              "visibility": "visible"
          },
          "paint": {
              "text-color": "#000",
              "text-halo-blur": 1,
              "text-halo-color": "rgba(255, 255, 255, 1)",
              "text-halo-width": 1
          },
          'filter': ['==', '$type', 'Polygon']
      });

      map.addLayer({
          'id': 'omradesnummertext',
          'type': 'symbol',
          'source': 'omraden',
          "layout": {
              "text-field": [
                  "concat",
                  "Omrnr: ",
                  ["get", "omradesnummer"],
              ],
              "text-font": ["Open Sans SemiBold"],
              "text-size": {
                  "stops": [
                      [2, 10],
                      [4, 12],
                      [6, 12],
                      [12, 12],
                      [16, 16]
                  ]
              },
              "visibility": "visible"
          },
          "paint": {
              "text-color": "#000",
              "text-halo-blur": 1,
              "text-halo-color": "rgba(255, 255, 255, 1)",
              "text-halo-width": 1
          },
          'filter': ['==', '$type', 'Polygon']
      });

      const bounds = coordinates.reduce((bounds, coord) => {
          return bounds.extend(coord);
      }, new maplibregl.LngLatBounds(coordinates[0], coordinates[0]));

      map.fitBounds(bounds, {
          padding: 120,
          duration: 0
      });

  });
</script>

Custom Styles

You can copy the style that we provide and make your own adjustments. We recommend using Maputnik which is a visual editor for MapLibre and MapBox style specifiation. It allows you to make modifications to your style and save the json style sheet and host it yourself.

Each source object in the style reference our tileserver, tiles2.tic.io, which requires CORS. Therefore ensure you have registered your origin. Each URL consists of the following format which contains four(4) parts in the URL that should be replaced https://tiles2.tic.io/database.schema.table.column e.g database, schema, table and column.

You have access to the following databases. They are updated daily and the tile server serves cached tiles.

Database Description
lm_belagenhetsadress Contains all information relating to an address
lm_fastighetsindelning Contains all information relating to a property
lm_top1m Contains topography 1:1M
lm_top10 Contains topography 1:10 000
naturalearth Contains Natural Earth
ormeo Contains extended databases such as the EBH which is a national database of contaminated areas

Check next section for a full data dictionary.

Data Dictionary

Each databases consists of geometric tables and in total they are 248. Each table then contains a range of columns that can be used to be displayed on the map. For simplicity we are adding a full data dictionary in PDF (582 pages) that you can download and easier search in and pick the fields you want to display.

Download the data dictionary here

Adding a source layer is very simple in the style sheet and we have made an example below.

Let’s say you want to show railroads in the map (enabled by default in our stylesheet), you check the PDF-dictionary and find that there is a table called ralstrafik (English: rail roads) and the column geom contains the geometry of the railroads.

Just add a source object with the nickname “ralstrafik” and you are done:

"ralstrafik": {
      "type": "vector",
      "url": "https://tiles2.tic.io/lm_top10.table.public.ralstrafik.geom"
    },

This should be interpreted as

Database = lm_top10 Schema = public Table = ralstrafik Column = geom