Real estate search on the map
Three search scenarios your visitors already expect on a real estate portal, ready to use in the JavaScript Map plugin: search by travel time, draw the search area, and lifestyle search. Plus a results map that displays your listings with the area they were found in.
What you get
- Travel-time search: a panel with a reference address (work, school), a travel-time slider and four travel modes. Yatmo draws the reachable area and hands you its polygon.
- Draw-a-zone search: the visitor draws the area with one stroke, no instructions needed. A "Search in this area" button triggers your site.
- Lifestyle search: "30 minutes from my work by car", "close to a primary school", "near metro line 1". Yatmo stores the criteria per visitor, computes the area matching all of them and keeps it until the criteria change.
- Listings on the map: the results page. Thousands of listings clustered natively, price pills, a base card (picture, price, title, place, link) you can restyle or replace, and the search area drawn around them.
The division of work is simple and deliberate: Yatmo computes the geography (isochrones, proximity to points of interest, stored criteria) and your backend searches the listings, because only you know your inventory, your filters and your business rules. Everything the map hands over is standard GeoJSON.
The flow, end to end
- Search page. Your page embeds the map with one or more scenarios enabled. The visitor picks an address and a travel time, draws an area, or saves lifestyle criteria. Yatmo draws the resulting area on the map.
- Hand-over. When the visitor clicks the search button, the plugin calls your
onSearch(shape, meta)callback (and fires ayatmo:searchDOM event) with the polygon as a GeoJSON geometry. - Your backend. You run your own listings query with a spatial filter on that polygon, exactly as you would with a bounding box today. Any database with a "point in polygon" function works (see below).
- Results page. Another page (or the same one) embeds the map with
listings: your results, and the same shape so the visitor sees where they searched.
map_v3.js only downloads when yatmoConfig declares one of them. Your property pages that use the map today keep their exact weight and behaviour.Enabling a scenario
Each scenario is one optional object in yatmoConfig. Several can be enabled on the same map, they stack in one panel.
<div id="map"></div>
<script>
yatmoConfig = {
licenseKey: 'xxxxxxxxxxxxxxxxxx',
language: 'FR',
country: 'BE',
container: 'map',
center: [4.3517, 50.8503],
zoom: 12,
startsWithPoisHidden: true, // a search map usually starts clean
accentColor: '#0a7cff', // your brand colour, used by every control
rounded: '10px', // corner radius of every Yatmo control
travelTimeSearch: { onSearch: runMySearch },
drawSearch: { onSearch: runMySearch },
lifestyleSearch: { userId: 'your-visitor-id', onSearch: runMySearch }
};
function runMySearch(shape, meta) {
// shape: GeoJSON Polygon or MultiPolygon (null when the area was cleared)
// meta.type: 'travelTime' | 'drawn' | 'lifestyle'
sessionStorage.setItem('searchShape', JSON.stringify(shape));
location.href = '/results';
}
</script>
<script src="https://map.yatmo.com/map_v3.js"></script>
Getting the shape back
Two callbacks, so you can tell a preview from a search intent:
| Name | In | Type | Description |
|---|---|---|---|
| onShape(shape, meta) | callback | function |
Fired every time the area changes: computed, redrawn, cleared (shape is then null). Use it for previews, counters, analytics.
|
| onSearch(shape, meta) | callback | function | Fired when the visitor clicks the search button of the panel. This is where you run your listings query. |
| yatmo:shape / yatmo:search | DOM event | CustomEvent |
The same two moments as DOM events on the map container, event.detail = { shape, meta }, bubbling. Handy with frameworks or tag managers.
|
| YatmoSearch.getShape() | runtime | function |
Returns the current area at any time (or null).
|
meta always carries type (travelTime, drawn or lifestyle) plus scenario-specific fields documented on each page. The shape is a GeoJSON geometry (not a Feature): { "type": "Polygon", "coordinates": [[[lng, lat], ...]] }, longitude first as in every GeoJSON. Travel-time and lifestyle areas are often MultiPolygons (a river or a motorway splits them).
document.getElementById('map').addEventListener('yatmo:search', function (e) {
console.log(e.detail.meta.type, e.detail.shape);
});
Filtering on your side
Store the GeoJSON with the search (session, URL parameter, query string of your API, whatever you already do for bounding boxes) and filter your listings with a point-in-polygon test. The shapes are simplified by Yatmo before being handed over (a few hundred vertices for a drawn area, a few thousand for a country-wide lifestyle criterion), so they are cheap to query.
-- PostgreSQL + PostGIS
SELECT * FROM listings
WHERE ST_Contains(ST_GeomFromGeoJSON(:shape), ST_SetSRID(ST_MakePoint(longitude, latitude), 4326));
-- SQL Server
DECLARE @area geography = geography::STGeomFromText(@wkt, 4326); -- convert the GeoJSON to WKT once, server side
SELECT * FROM listings WHERE @area.STIntersects(geography::Point(latitude, longitude, 4326)) = 1;
-- MySQL 8
SELECT * FROM listings
WHERE ST_Contains(ST_GeomFromGeoJSON(:shape), ST_SRID(POINT(longitude, latitude), 4326));
Search engines do it too: Elasticsearch geo_polygon / geo_shape queries, Algolia insidePolygon, Meilisearch _geoPolygon. If your stack cannot do spatial queries, pre-filter with the polygon's bounding box and test the remaining points in code with any ray-casting helper.
Styling and CSS isolation
The plugin protects itself from your site's generic CSS and, at the same time, lets you restyle anything on purpose:
- Every rule is scoped under the
[data-yatmo-root]attribute set on your container, with resets on buttons, inputs, headings, tables and text (abody { text-transform: uppercase }or a globalbutton { border: 3px dashed red }on your site does not reach the map). - Every element carries a
yatmo-prefixed class (yatmo-search-panel,yatmo-ls-card,yatmo-listing-pill, ...). A more specific selector on your side wins:#map [data-yatmo-root] .yatmo-search-panel { background: #1f2430; }. - Brand colours and radii come from config or CSS variables:
accentColor/accentForeground/roundedinyatmoConfig, or--yatmo-accent,--yatmo-accent-fg,--yatmo-radius,--yatmo-search-radius,--yatmo-fonton[data-yatmo-root]. - The listing card can be replaced entirely with
listings.renderCard(see Listings on the map).
/* A dark, branded panel: your selectors are more specific than the plugin's, so they win */
#map [data-yatmo-root] {
--yatmo-accent: #e8590c;
--yatmo-search-radius: 18px;
}
#map [data-yatmo-root] .yatmo-search-panel { background: #1f2430; color: #e6e6e6; }
#map [data-yatmo-root] .yatmo-search-title { color: #fff; }
#map [data-yatmo-root] .yatmo-search-input { background: #2a3040; color: #fff; border-color: #444c60; }
* { letter-spacing: 2px !important } beats any scoped rule by definition. Keep !important off your generic selectors, or scope them outside the map container.Languages
The panels are translated in the 23 languages of the plugin, selected by yatmoConfig.language. Category names ("Primary school", "Metro station") and transit line names come from the Yatmo API in that language, in every country we cover. Prices, titles and places of your listings are yours: the plugin displays the strings you give it, so format them the way your site does.
Performance
- The companion module (about 65 KB) and its stylesheet (15 KB) are loaded only when a search feature is configured, after the map itself.
- Listings are rendered as clustered map layers; HTML price pills are only created for the de-clustered points currently on screen (250 at most by default). Twenty thousand listings stay smooth.
- The lifestyle area is computed once per set of criteria and stored on the Yatmo side: the next visit reuses it.
- Transit line search runs on the Yatmo API (thirty thousand lines in France alone), the browser only receives the eight best matches.