From c3b2872b7cff81165fcf6f2f775244d1d6b0a164 Mon Sep 17 00:00:00 2001 From: Esteban Ordano Date: Wed, 29 Jul 2026 19:08:41 +0200 Subject: [PATCH] feat: stop demarcating districts on the atlas Districts are drawn as the LAND they are, and the borders of their parcels are recomputed against the estate each one belongs to instead of tracing the district outline. Parcels on sale go through the same path because the v1 tiles endpoint reports the ones inside a district as any other parcel on sale. Enacts DAO proposal 9ee1965f-6a96-45f9-bb20-f60baa13607f. --- src/components/Atlas/Atlas.spec.tsx | 113 ++++++++++++++++++++++++++++ src/components/Atlas/Atlas.tsx | 28 ++++++- 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/components/Atlas/Atlas.spec.tsx diff --git a/src/components/Atlas/Atlas.spec.tsx b/src/components/Atlas/Atlas.spec.tsx new file mode 100644 index 00000000..a5bab03f --- /dev/null +++ b/src/components/Atlas/Atlas.spec.tsx @@ -0,0 +1,113 @@ +import { Atlas, AtlasProps, AtlasTile } from './Atlas' + +const OWNED_COLOR = '#3D3A46' +const UNOWNED_COLOR = '#09080A' +const ROAD_COLOR = '#716C7A' + +const buildTile = (tile: Partial = {}): AtlasTile => ({ + x: 0, + y: 0, + type: 9, + owner: '0xowner', + ...tile +}) + +const buildLayer = (tiles: AtlasTile[]) => { + const props = { + tiles: tiles.reduce>((acc, tile) => { + acc[tile.x + ',' + tile.y] = tile + return acc + }, {}) + } as AtlasProps + return new Atlas(props).layer +} + +describe('Atlas.layer', () => { + describe('when the tile is a road', () => { + it('should keep its color and the borders reported by the API', () => { + const layer = buildLayer([buildTile({ type: 7, top: 1, left: 1 })]) + + expect(layer(0, 0)).toEqual({ + color: ROAD_COLOR, + top: true, + left: true, + topLeft: false + }) + }) + }) + + describe('when the tile is a district', () => { + it('should paint it as the LAND it is instead of demarcating it', () => { + const layer = buildLayer([buildTile({ type: 5 })]) + + expect(layer(0, 0)).toEqual({ + color: OWNED_COLOR, + top: false, + left: false, + topLeft: false + }) + }) + + it('should paint an unowned district parcel as unowned LAND', () => { + const layer = buildLayer([ + buildTile({ type: 5, owner: undefined as never }) + ]) + + expect(layer(0, 0)).toHaveProperty('color', UNOWNED_COLOR) + }) + + it('should stitch it to the neighbours that belong to its estate', () => { + const layer = buildLayer([ + buildTile({ + x: 0, + y: 0, + type: 5, + estate_id: '1', + top: 1, + left: 1, + topLeft: 1 + }), + buildTile({ x: 0, y: 1, type: 5, estate_id: '1' }), + buildTile({ x: -1, y: 0, type: 5, estate_id: '2' }), + buildTile({ x: -1, y: 1, type: 5, estate_id: '1' }) + ]) + + expect(layer(0, 0)).toEqual({ + color: OWNED_COLOR, + top: true, + left: false, + topLeft: true + }) + }) + + it('should not stitch a district parcel that does not belong to an estate', () => { + const layer = buildLayer([ + buildTile({ x: 0, y: 0, type: 5, top: 1, left: 1, topLeft: 1 }), + buildTile({ x: 0, y: 1, type: 5 }) + ]) + + expect(layer(0, 0)).toEqual({ + color: OWNED_COLOR, + top: false, + left: false, + topLeft: false + }) + }) + }) + + describe('when the tile is a parcel on sale inside a district', () => { + it('should stitch it to its estate instead of to the district', () => { + const layer = buildLayer([ + buildTile({ x: 0, y: 0, type: 10, estate_id: '1', top: 1, left: 1 }), + buildTile({ x: -1, y: 0, type: 5, estate_id: '2' }) + ]) + + expect(layer(0, 0)).toEqual({ + color: OWNED_COLOR, + top: false, + left: false, + topLeft: false + }) + }) + }) +}) diff --git a/src/components/Atlas/Atlas.tsx b/src/components/Atlas/Atlas.tsx index dc7b696f..f7c27ba1 100644 --- a/src/components/Atlas/Atlas.tsx +++ b/src/components/Atlas/Atlas.tsx @@ -17,6 +17,17 @@ export type AtlasTile = { export { Layer, Coord } +// Legacy tile types served by https://api.decentraland.org/v1/tiles +const DISTRICT_TYPE = 5 +const OWNED_TYPE = 9 +const ON_SALE_TYPE = 10 +const UNOWNED_TYPE = 11 + +const coordsToId = (x: number, y: number) => x + ',' + y + +const isSameEstate = (tile: AtlasTile, other?: AtlasTile) => + !!tile.estate_id && !!other && other.estate_id === tile.estate_id + export type AtlasProps = Omit & { layers?: Layer[] tiles?: Record @@ -32,7 +43,7 @@ const COLOR_BY_TYPE = Object.freeze({ 2: '#ff9990', // my estates 3: '#ff4053', // my estates on sale 4: '#ffbd33', // parcels/estates where I have permissions - 5: '#5054D4', // districts + 5: '#3D3A46', // districts, drawn as owned LAND 6: '#563db8', // contributions 7: '#716C7A', // roads 8: '#70AC76', // plazas @@ -64,6 +75,21 @@ export class Atlas extends React.PureComponent { const id = x + ',' + y if (tiles && id in tiles) { const tile = tiles[id] + // Districts are drawn as the LAND they are, with borders recomputed per + // estate instead of tracing the district outline. Parcels on sale go + // through it too: the ones inside a district are reported as ON_SALE. + // See DAO proposal 9ee1965f-6a96-45f9-bb20-f60baa13607f. + if (tile.type === DISTRICT_TYPE || tile.type === ON_SALE_TYPE) { + return { + color: + tile.type === DISTRICT_TYPE + ? COLOR_BY_TYPE[tile.owner ? OWNED_TYPE : UNOWNED_TYPE] + : COLOR_BY_TYPE[tile.type], + top: isSameEstate(tile, tiles[coordsToId(x, y + 1)]), + left: isSameEstate(tile, tiles[coordsToId(x - 1, y)]), + topLeft: isSameEstate(tile, tiles[coordsToId(x - 1, y + 1)]) + } + } return { color: COLOR_BY_TYPE[tile.type], top: !!tile.top,