Skip to content

Commit c6a9f2c

Browse files
authored
Merge pull request #54 from geolonia/feature/issue-657-geoproperty-table-display
fix(cli): format GeoProperty as readable coordinates in table display
2 parents b947107 + 0629181 commit c6a9f2c

2 files changed

Lines changed: 148 additions & 5 deletions

File tree

src/output.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,38 @@ function collectKeys(items: Record<string, unknown>[]): string[] {
9898
return sorted;
9999
}
100100

101+
function formatGeoJSON(geo: Record<string, unknown>): string {
102+
const geoType = String(geo.type);
103+
const coords = geo.coordinates;
104+
if (geoType === "Point" && Array.isArray(coords) && coords.length >= 2) {
105+
return `Point(${Number(coords[0]).toFixed(2)}, ${Number(coords[1]).toFixed(2)})`;
106+
}
107+
if (Array.isArray(coords)) {
108+
const count = geoType === "Polygon"
109+
? (Array.isArray(coords[0]) ? (coords[0] as unknown[]).length : 0)
110+
: coords.length;
111+
return `${geoType}(${count} coords)`;
112+
}
113+
return `${geoType}(...)`;
114+
}
115+
116+
function isGeoJSON(v: unknown): boolean {
117+
if (typeof v !== "object" || v === null) return false;
118+
const o = v as Record<string, unknown>;
119+
return typeof o.type === "string" && "coordinates" in o;
120+
}
121+
101122
function cellValue(val: unknown): string {
102123
if (val === undefined || val === null) return "";
103-
if (typeof val === "object") {
104-
const obj = val as Record<string, unknown>;
105-
if ("value" in obj) return String(obj.value);
106-
return JSON.stringify(val);
124+
if (typeof val !== "object") return String(val);
125+
const obj = val as Record<string, unknown>;
126+
if (isGeoJSON(obj)) return formatGeoJSON(obj);
127+
if ("value" in obj) {
128+
const v = obj.value;
129+
if (isGeoJSON(v)) return formatGeoJSON(v as Record<string, unknown>);
130+
return String(v);
107131
}
108-
return String(val);
132+
return JSON.stringify(val);
109133
}
110134

111135
function toGeoJSON(data: unknown): unknown {

tests/output.test.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,125 @@ describe("output", () => {
101101
expect(result).toContain('{"nested":true,"count":5}');
102102
});
103103

104+
it("formats GeoProperty Point as readable coordinates (NGSIv2)", () => {
105+
const data = [
106+
{
107+
id: "e1",
108+
location: {
109+
type: "geo:json",
110+
value: { type: "Point", coordinates: [139.7, 35.6] },
111+
},
112+
},
113+
];
114+
const result = stripAnsi(formatOutput(data, "table"));
115+
expect(result).toContain("Point(139.70, 35.60)");
116+
});
117+
118+
it("formats GeoProperty Point as readable coordinates (NGSI-LD)", () => {
119+
const data = [
120+
{
121+
id: "e1",
122+
location: {
123+
type: "GeoProperty",
124+
value: { type: "Point", coordinates: [139.7, 35.6] },
125+
},
126+
},
127+
];
128+
const result = stripAnsi(formatOutput(data, "table"));
129+
expect(result).toContain("Point(139.70, 35.60)");
130+
});
131+
132+
it("formats GeoJSON directly in keyValues mode", () => {
133+
const data = [
134+
{
135+
id: "e1",
136+
location: { type: "Point", coordinates: [139.7, 35.6] },
137+
},
138+
];
139+
const result = stripAnsi(formatOutput(data, "table"));
140+
expect(result).toContain("Point(139.70, 35.60)");
141+
});
142+
143+
it("formats LineString with coordinate count", () => {
144+
const data = [
145+
{
146+
id: "e1",
147+
route: {
148+
type: "GeoProperty",
149+
value: {
150+
type: "LineString",
151+
coordinates: [
152+
[139.7, 35.6],
153+
[139.8, 35.7],
154+
[139.9, 35.8],
155+
],
156+
},
157+
},
158+
},
159+
];
160+
const result = stripAnsi(formatOutput(data, "table"));
161+
expect(result).toContain("LineString(3 coords)");
162+
});
163+
164+
it("formats Polygon with coordinate count", () => {
165+
const data = [
166+
{
167+
id: "e1",
168+
area: {
169+
type: "GeoProperty",
170+
value: {
171+
type: "Polygon",
172+
coordinates: [
173+
[
174+
[139.7, 35.6],
175+
[139.8, 35.6],
176+
[139.8, 35.7],
177+
[139.7, 35.6],
178+
],
179+
],
180+
},
181+
},
182+
},
183+
];
184+
const result = stripAnsi(formatOutput(data, "table"));
185+
expect(result).toContain("Polygon(4 coords)");
186+
});
187+
188+
it("formats MultiPoint with coordinate count", () => {
189+
const data = [
190+
{
191+
id: "e1",
192+
stops: {
193+
type: "GeoProperty",
194+
value: {
195+
type: "MultiPoint",
196+
coordinates: [
197+
[139.7, 35.6],
198+
[139.8, 35.7],
199+
[139.9, 35.8],
200+
],
201+
},
202+
},
203+
},
204+
];
205+
const result = stripAnsi(formatOutput(data, "table"));
206+
expect(result).toContain("MultiPoint(3 coords)");
207+
});
208+
209+
it("handles Polygon with empty coordinates", () => {
210+
const data = [
211+
{
212+
id: "e1",
213+
area: {
214+
type: "GeoProperty",
215+
value: { type: "Polygon", coordinates: [] },
216+
},
217+
},
218+
];
219+
const result = stripAnsi(formatOutput(data, "table"));
220+
expect(result).toContain("Polygon(0 coords)");
221+
});
222+
104223
it("returns empty string for null/undefined values", () => {
105224
const data = [{ id: "e1", n: null, u: undefined }];
106225
const result = stripAnsi(formatOutput(data, "table"));

0 commit comments

Comments
 (0)