Like any good 20-something, I like to live life and have my little splurges: getting a good job, moving out, saving for retirement, and buying a home. Fortunately, most of these goals are well on track, but buying a home is proving elusive. Sometimes I get frustrated with it because I feel I’m not doing enough to achieve it, but then I look at interest rates and it passes.
In all my adventures and misadventures in the search for a property I can afford, there is one constant: the sea of tears involved in looking up cadastral information. An afternoon of property hunting usually looks like this:
- Doing doomscrolling through a myriad of ads that don’t fit my needs
(a.k.a. I’d have to sell my soul to the devil) - Finding a property that might work for me
- Checking the information against the cadastre, especially for land plots
(Yes, I value self-building. Am I crazy? I know) . Normally there is no cadastral reference provided, and you have tosufferuse the map on the website - Go back to point 1
“But Juan, why do you look all this up in the cadastre if it’s usually detailed in the ad?” Once I reported an ad for listing a
plot of land as urban when the cadastral information for the land (provided in the ad) listed it as rustic.
They proceeded to remove the cadastral information from the ad instead of listing it correctly. You can’t trust anyone nowadays,
especially with something that can mortgage you
User experience
I present to you the Spanish Cadastre map. At first glance, it may seem like the map is responsive. I invite the reader to zoom in until the cadastral information starts to appear:

As soon as you move the map, you can see a loading symbol. This repeats if you want to change the view which, honestly, is quite annoying. If we compare it with the user experience of Google Maps, it leaves much to be desired 😢:
How does the cadastre work?
By occupational defect, seeing a computer system not working properly catches my attention and, if I have to use it frequently, I need to try
to fix it
HTTP Requests
As a backender, my first instinct is to go to what I know: API requests:
As can be observed, several requests are used to render the map:
- Images taken from tms-ign-base.idee.es which are quite efficient 👍
- Images taken from sedecatastro.gob.es which are not 👎
The fact that a map image has a latency of almost 1 second is not a good omen. Let’s take a look at several of the request parameters:
SERVICE=WMSWIDTH=1920HEIGHT=425BBOX=-434969.6867710633,5381250.394276095,-425797.2433768422,5383280.75283992There are several things to highlight in these parameters:
- The WIDTH and HEIGHT parameters clearly show a request for an image the size of the browser. This contrasts with the way Google Maps or tms-ign-base.idee.es queries, as they receive several images per render.
- BBOX, being a continuous rather than discrete range, suggests that the server performs the rendering at the time of the request. The way the rest of the requests are made, being discrete and following a directory structure, points to a content server that simply serves stored files.
- The SERVICE parameter points to a protocol or backend server specification. We’ll come back to this later.
The main conclusion that can be drawn from this analysis is that the request interface is not very cache friendly. Every time the map view is moved, the entire image needs to be re-rendered. Furthermore, images outside the viewport cannot be pre-rendered as in tiling-based maps. These factors make the user experience very clumsy.
Can we fix it on the client side?
The hypothesis of the previous section suggests that the requests are not very optimized. It might seem at first glance that we can’t do anything about it, but… turns out we can!.
Let me introduce you to Leaflet.js, a library for integrating interactive maps using different servers. While reading the documentation of the library to integrate the idee.es view, I discovered something called TileLayer.WMS. Does that ring a bell? Exactly. It’s the SERVICE parameter of the request. This suggests that the backend uses a defined standard and probably some open-source software to serve the information.
Let’s try to create a small JS component to render the cadastre information:
var map = L.map('map').setView([43.454, -3.823], 18);var IGN = L.tileLayer('https://tms-ign-base.idee.es/1.0.0/IGNBaseTodo/{z}/{x}/{-y}.jpeg', {maxZoom: 17});IGN.addTo(map);var ortofoto = L.tileLayer('https://tms-pnoa-ma.idee.es/1.0.0/pnoa-ma/{z}/{x}/{-y}.jpeg');ortofoto.addTo(map);var catastro = L.tileLayer.wms("https://www1.sedecatastro.gob.es/cartografia/GeneraMapa.aspx", { layers: 'CATASTRO', format: 'image/png', transparent: true});var baseMaps = { "IGN": IGN, "Ortofoto": ortofoto};var overlayMaps = { "Catastro": catastro}var layerControl = L.control.layers(baseMaps, overlayMaps).addTo(map);Here is the map prototype that allows rendering both the satellite map and the cadastral information. On the top right, you can toggle the different layers. If you try zooming in and activating the cadastre layer, you can see a great improvement in performance.
⚠️ The cadastre server has rate limits on the number of requests. Since the client uses a tiling process, it makes many more requests than usual and can trigger the rate limit. I do not recommend using this solution for a long time (it is here for illustrative purposes) and I definitely don’t say this from personal experience 😇
The root of the problem
Bingo. So a very simple client can be created that runs smoothly with the backend. Then… Why is the web experience so dire?
The JavaScript code for rendering the cadastre map can be accessed through this link. One of the important things it refers to is a library called OpenLayers. This library is, in essence, an alternative to LeafLet for visualizing maps.
Let’s see how it uses this library:
771 var vectorLayer = new ol.layer.Vector({ title: 'Posicionar', source: vectorSource });
777 var map = new ol.Map({778 interactions: interaccionesxDefecto,779 controls: ([pantallaCompleta, rotor, EscalaLineal, PosicionRatonGPS]),780 target: 'map',782 layers: [vectorLayer],783 moveTolerance: 5,784 view: vistaMapa785 });As you can see, currently the cadastre makes inefficient use of the library by using a vector layer. As we saw before, it requests cadastral information with a single request for everything that needs to be rendered at that moment.
If we substitute the map definition with:
779 var map = new ol.Map({780 interactions: interaccionesxDefecto,781 controls: ([pantallaCompleta, rotor, EscalaLineal, PosicionRatonGPS]),782 target: 'map',783 layers: [layerIGNT, layerTeseladaTamanio, layerTeselada],784 moveTolerance: 5,785 view: vistaMapa786 });and activate the layer in the program loop:
if (ponerCAT && ponerCATEscala) { map.addLayer(layer1Imagen); map.addLayer(layerTeselada);}if (ponerGeoServerWMSR) { map.addLayer(layerGeoserverWMSR);};if (ponerGeoServerWMTSR) { map.addLayer(layerGeoserverWMTSR);};if (soloTematicos) { if (ponerTematicos) { map.removeLayer(layer1Imagen); map.removeLayer(layerTeselada); map.addLayer(layer1Tematicos); };}We achieve that the client gets the map in a tiled way 🎉🎉🎉
But wouldn’t tiling bombard the server with requests?
Yes and no. Effectively, with the current server infrastructure, needing to render every image returned by the tiling
could cause a denial of service
sequenceDiagram
Participant WMS
Participant Frontend
Actor User
User ->> Frontend: Moves the view
Frontend ->> WMS: HTTP GET PNG
WMS -->> WMS: Rendering view (1 sec delay)....
WMS -->> Frontend: HTTP PNG Response
Frontend ->> User: The user sees the map update
However, if the tiling used an XYZ protocol like Google or IDEE does, it would be easily convertible into a content delivery problem, which is inherently less costly:
sequenceDiagram
Participant WMS
participant Cache
participant API
Participant Frontend
Actor User
User ->> Frontend: Moves the view
Frontend ->> API: HTTP GET XYZ PNG
API ->> Cache: HTTP GET XYZ PNG
Cache -->> API: HTTP PNG Response
alt If the tile is not present
API ->> WMS: HTTP GET PNG
WMS -->> WMS: Rendering view (1 sec delay)....
WMS -->> API: HTTP PNG Response
API ->> Cache: Save the image in the cache
end
API -->> Frontend: HTTP PNG Response
Frontend ->> User: The user sees the map update
With this solution, the cadastre website could have the fluidity we achieved in the Javascript example without incurring a computational overhead by launching several simultaneous renderings. Furthermore, a change in the backend or the infrastructure with which the data is uploaded and updated would not be necessary.
In fact, simply invalidating the cache when an update of the data for that region occurs would be enough to have updated data.
Next steps
After this analysis and without involving the cadastre, one could try to set up an optimized visualization server with the open data from the cadastre. However, the bulk dump of the cadastre data is updated twice a year and, without obtaining real-time updates, the map would quickly become outdated.
Another option would be to set up the cache on top of the public API of the WMS server. It would be the most viable option since it would allow obtaining real-time updates simply
by invalidating the cache. However, the public API of the cadastre has rate_limits
So, for now, this mini project goes into the drawer of paused projects waiting for some charitable soul to use this post to fix the UX of the website.