From 451c8570ad210aaec75b9240cbe1c30e8a0cd7a4 Mon Sep 17 00:00:00 2001 From: Adrian Lara <adrian@devetry.com> Date: Tue, 13 Oct 2020 11:05:28 -0400 Subject: [PATCH 1/3] Add property upload GeoJSON button --- src/app/app.module.ts | 4 +- src/app/home/home.component.html | 2 + src/app/home/home.component.scss | 2 +- .../properties-upload.component.html | 8 ++++ .../properties-upload.component.scss | 0 .../properties-upload.component.spec.ts | 25 +++++++++++ .../properties-upload.component.ts | 42 +++++++++++++++++++ 7 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/app/properties-upload/properties-upload.component.html create mode 100644 src/app/properties-upload/properties-upload.component.scss create mode 100644 src/app/properties-upload/properties-upload.component.spec.ts create mode 100644 src/app/properties-upload/properties-upload.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 5e99006..66a79a4 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -20,6 +20,7 @@ import { MaterialModule } from './material-module'; import { SharedModule } from './shared/shared.module'; import { MapComponent } from './map/map.component'; import { InventoryComponent } from './inventory/inventory.component'; +import { PropertiesUploadComponent } from './properties-upload/properties-upload.component'; // AoT requires an exported function for factories export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader { @@ -33,7 +34,8 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader { HeaderComponent, LeftNavComponent, MapComponent, - InventoryComponent + InventoryComponent, + PropertiesUploadComponent ], imports: [ AppRoutingModule, diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index f582814..83a27b1 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -15,4 +15,6 @@ <h1 class="title">Running: {{ status.running }}</h1> </div> <textarea disabled>{{ latestQueryResult }}</textarea> + + <app-properties-upload></app-properties-upload> </div> diff --git a/src/app/home/home.component.scss b/src/app/home/home.component.scss index 99a01f7..925d112 100644 --- a/src/app/home/home.component.scss +++ b/src/app/home/home.component.scss @@ -10,5 +10,5 @@ textarea { width: 600px; - height: 400px; + height: 200px; } diff --git a/src/app/properties-upload/properties-upload.component.html b/src/app/properties-upload/properties-upload.component.html new file mode 100644 index 0000000..79e6b22 --- /dev/null +++ b/src/app/properties-upload/properties-upload.component.html @@ -0,0 +1,8 @@ +<div> + <label for="file">Choose building GeoJSON file</label> + <input + type="file" + id="file" + accept=".geojson" + (change)="handleFileInput($event.target.files)"> +</div> diff --git a/src/app/properties-upload/properties-upload.component.scss b/src/app/properties-upload/properties-upload.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/properties-upload/properties-upload.component.spec.ts b/src/app/properties-upload/properties-upload.component.spec.ts new file mode 100644 index 0000000..b8144a6 --- /dev/null +++ b/src/app/properties-upload/properties-upload.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PropertiesUploadComponent } from './properties-upload.component'; + +describe('PropertiesUploadComponent', () => { + let component: PropertiesUploadComponent; + let fixture: ComponentFixture<PropertiesUploadComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PropertiesUploadComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PropertiesUploadComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/properties-upload/properties-upload.component.ts b/src/app/properties-upload/properties-upload.component.ts new file mode 100644 index 0000000..d0fd403 --- /dev/null +++ b/src/app/properties-upload/properties-upload.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import { PropertyService } from '../core/services/property/property.service'; + + +@Component({ + selector: 'app-properties-upload', + templateUrl: './properties-upload.component.html', + styleUrls: ['./properties-upload.component.scss'] +}) +export class PropertiesUploadComponent implements OnInit { + constructor(private propertyService: PropertyService) {} + + ngOnInit(): void { + } + + handleFileInput(files: FileList): void { + if (files[0]) { + files[0].text().then(text => { + const geojson = JSON.parse(text); + const crs = geojson.crs ? geojson.crs : { type: 'name', properties: { name: 'EPSG:4326'} }; + + geojson.features.forEach(feature => { + const extra_data = feature.properties; + feature.geometry.crs = crs; + + // extract non-extra_data attributes + const ubid = extra_data.UBID; + delete extra_data.UBID; + + this.propertyService.model.create({ + extra_data: extra_data, + footprint: feature.geometry, + ubid: ubid + }).catch((err) => { + console.error(err); + }); + }); + }) + } + } + +} From 84bd0d3e4f8828d875c244d764493db899c69917 Mon Sep 17 00:00:00 2001 From: Adrian Lara <adrian@devetry.com> Date: Tue, 13 Oct 2020 11:18:28 -0400 Subject: [PATCH 2/3] Add tax lot upload GeoJSON button --- src/app/app.module.ts | 4 +- src/app/home/home.component.html | 1 + .../properties-upload.component.ts | 3 +- .../taxlots-upload.component.html | 8 ++++ .../taxlots-upload.component.scss | 0 .../taxlots-upload.component.spec.ts | 25 +++++++++++ .../taxlots-upload.component.ts | 42 +++++++++++++++++++ 7 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/app/taxlots-upload/taxlots-upload.component.html create mode 100644 src/app/taxlots-upload/taxlots-upload.component.scss create mode 100644 src/app/taxlots-upload/taxlots-upload.component.spec.ts create mode 100644 src/app/taxlots-upload/taxlots-upload.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 66a79a4..7b16fb4 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -21,6 +21,7 @@ import { SharedModule } from './shared/shared.module'; import { MapComponent } from './map/map.component'; import { InventoryComponent } from './inventory/inventory.component'; import { PropertiesUploadComponent } from './properties-upload/properties-upload.component'; +import { TaxlotsUploadComponent } from './taxlots-upload/taxlots-upload.component'; // AoT requires an exported function for factories export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader { @@ -35,7 +36,8 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader { LeftNavComponent, MapComponent, InventoryComponent, - PropertiesUploadComponent + PropertiesUploadComponent, + TaxlotsUploadComponent ], imports: [ AppRoutingModule, diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 83a27b1..91462b7 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -17,4 +17,5 @@ <h1 class="title">Running: {{ status.running }}</h1> <textarea disabled>{{ latestQueryResult }}</textarea> <app-properties-upload></app-properties-upload> + <app-taxlots-upload></app-taxlots-upload> </div> diff --git a/src/app/properties-upload/properties-upload.component.ts b/src/app/properties-upload/properties-upload.component.ts index d0fd403..be787e5 100644 --- a/src/app/properties-upload/properties-upload.component.ts +++ b/src/app/properties-upload/properties-upload.component.ts @@ -8,7 +8,8 @@ import { PropertyService } from '../core/services/property/property.service'; styleUrls: ['./properties-upload.component.scss'] }) export class PropertiesUploadComponent implements OnInit { - constructor(private propertyService: PropertyService) {} + + constructor(private propertyService: PropertyService) { } ngOnInit(): void { } diff --git a/src/app/taxlots-upload/taxlots-upload.component.html b/src/app/taxlots-upload/taxlots-upload.component.html new file mode 100644 index 0000000..7cfd675 --- /dev/null +++ b/src/app/taxlots-upload/taxlots-upload.component.html @@ -0,0 +1,8 @@ +<div> + <label for="file">Choose tax lot GeoJSON file</label> + <input + type="file" + id="file" + accept=".geojson" + (change)="handleFileInput($event.target.files)"> +</div> diff --git a/src/app/taxlots-upload/taxlots-upload.component.scss b/src/app/taxlots-upload/taxlots-upload.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/taxlots-upload/taxlots-upload.component.spec.ts b/src/app/taxlots-upload/taxlots-upload.component.spec.ts new file mode 100644 index 0000000..305003b --- /dev/null +++ b/src/app/taxlots-upload/taxlots-upload.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TaxlotsUploadComponent } from './taxlots-upload.component'; + +describe('TaxlotsUploadComponent', () => { + let component: TaxlotsUploadComponent; + let fixture: ComponentFixture<TaxlotsUploadComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ TaxlotsUploadComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TaxlotsUploadComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/taxlots-upload/taxlots-upload.component.ts b/src/app/taxlots-upload/taxlots-upload.component.ts new file mode 100644 index 0000000..33b8782 --- /dev/null +++ b/src/app/taxlots-upload/taxlots-upload.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit } from '@angular/core'; +import { TaxLotService } from '../core/services/tax-lot/tax-lot.service'; + +@Component({ + selector: 'app-taxlots-upload', + templateUrl: './taxlots-upload.component.html', + styleUrls: ['./taxlots-upload.component.scss'] +}) +export class TaxlotsUploadComponent implements OnInit { + + constructor(private taxlotService: TaxLotService) { } + + ngOnInit(): void { + } + + handleFileInput(files: FileList): void { + if (files[0]) { + files[0].text().then(text => { + const geojson = JSON.parse(text); + const crs = geojson.crs ? geojson.crs : { type: 'name', properties: { name: 'EPSG:4326'} }; + + geojson.features.forEach(feature => { + const extra_data = feature.properties; + feature.geometry.crs = crs; + + // extract non-extra_data attributes + const ubid = extra_data.UBID; + delete extra_data.UBID; + + this.taxlotService.model.create({ + extra_data: extra_data, + footprint: feature.geometry, + ulid: ubid + }).catch((err) => { + console.error(err); + }); + }); + }) + } + } + +} From 542cea4d90779fe7a30582d7345bdd75662a2b5a Mon Sep 17 00:00:00 2001 From: Adrian Lara <adrian@devetry.com> Date: Wed, 14 Oct 2020 09:43:52 -0400 Subject: [PATCH 3/3] Inventory map page tweaks - fix axis order - show extra_data to help with analysis --- src/app/map/map.component.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/app/map/map.component.ts b/src/app/map/map.component.ts index 3ada372..95e8c8d 100644 --- a/src/app/map/map.component.ts +++ b/src/app/map/map.component.ts @@ -53,12 +53,12 @@ export class MapComponent implements OnInit { values[0].forEach(property => { if (property.footprint) { const footprintCoords = property.footprint.coordinates[0].map(coords => { - return new Microsoft.Maps.Location(...coords) + return new Microsoft.Maps.Location(...coords.reverse()) }) const polygon = new Microsoft.Maps.Polygon(footprintCoords); const infobox = new Microsoft.Maps.Infobox(footprintCoords[0], { title: 'Property Information', - description: property.ubid, + description: JSON.stringify(property.extra_data), visible: false }); infobox.setMap(this.map); @@ -80,12 +80,12 @@ export class MapComponent implements OnInit { values[1].forEach(taxlot => { if (taxlot.footprint) { const footprintCoords = taxlot.footprint.coordinates[0].map(coords => { - return new Microsoft.Maps.Location(...coords) + return new Microsoft.Maps.Location(...coords.reverse()) }) const polygon = new Microsoft.Maps.Polygon(footprintCoords, this.styles.polygonOptions); const infobox = new Microsoft.Maps.Infobox(footprintCoords[0], { title: 'Tax Lot Information', - description: taxlot.ulid, + description: JSON.stringify(taxlot.extra_data), visible: false }); infobox.setMap(this.map); @@ -106,10 +106,12 @@ export class MapComponent implements OnInit { this.map.layers.insert(propertyLayer); this.map.layers.insert(taxlotLayer); - // allFootprints used because can't find attribute of map or layers to get shapes - this.map.setView({ - bounds: new Microsoft.Maps.LocationRect.fromShapes(allFootprints) - }) + if (allFootprints.length) { + // allFootprints used because can't find attribute of map or layers to get shapes + this.map.setView({ + bounds: new Microsoft.Maps.LocationRect.fromShapes(allFootprints) + }); + } }) } }