d3.js 还是 rxjs 错误? this.svg.selectAll(...).data(...).enter 不是函数
d3.js or rxjs error? this.svg.selectAll(...).data(...).enter is not a function
这很奇怪。它也有点长,所以提前道歉。
更新 - 结果是 2 个问题,请参阅下面的回答。
这是我的错误:EXCEPTION: this.svg.selectAll(...).data(...).enter is not a function
我有一个 angular-cli 客户端和一个节点 api 服务器。我可以使用可观察对象(下面的代码)从服务中检索 states.json 文件。 d3 喜欢该文件并显示预期的美国地图。
当我将 api 服务器中的服务目标从文件更改为 bluemix-cloudant 服务器时,我的客户端出现了上述错误。
当我 console.log 使用 ngOnInit 的变体中的输出时,最初 mapData 打印为空数组并抛出错误。 这是明显的来源错误,因为没有数据,但 Chrome 调试器显示 get 请求挂起。请求完成后,数据将按预期打印在控制台中。
- angular-cli 版本 1.0.0-beta.26
- angular版本^2.3.1
- d3 版本 ^4.4.4
- rxjs 版本 ^5.0.1
map.component.ts:
import { Component, ElementRef, Input } from '@angular/core';
import * as D3 from 'd3';
import '../rxjs-operators';
import { MapService } from '../map.service';
@Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent {
errorMessage: string;
height;
host;
htmlElement: HTMLElement;
mapData;
margin;
projection;
path;
svg;
width;
constructor (private _element: ElementRef, private _mapService: MapService) {
this.host = D3.select(this._element.nativeElement);
this.getMapData();
this.setup();
this.buildSVG();
}
getMapData() {
this._mapService.getMapData()
.subscribe(
mapData => this.setMap(mapData),
error => this.errorMessage = <any>error
)
}
setup() {
this.margin = {
top: 15,
right: 50,
bottom: 40,
left: 50
};
this.width = document.querySelector('#map').clientWidth - this.margin.left - this.margin.right;
this.height = this.width * 0.6 - this.margin.bottom - this.margin.top;
}
buildSVG() {
this.host.html('');
this.svg = this.host.append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
}
setMap(mapData) {
this.mapData = mapData;
this.projection = D3.geoAlbersUsa()
.translate([this.width /2 , this.height /2 ])
.scale(650);
this.path = D3.geoPath()
.projection(this.projection);
this.svg.selectAll('path')
.data(this.mapData.features)
.enter().append('path')
.attr('d', this.path)
.style('stroke', '#fff')
.style('stroke-width', '1')
.style('fill', 'lightgrey');
}
}
map.service.ts:
import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class MapService {
private url = 'http://localhost:3000/api/mapData';
private socket;
constructor (private _http: Http) { }
getMapData(): Observable<any> {
return this._http.get(this.url)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
private handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg);
return Promise.reject(errMsg);
}
}
这个函数是异步的,d3 调用数据的时间太长了吗?
我本来希望这个问题
会提供一些见解,但我没有看到任何见解。
非常感谢任何帮助或见解!
编辑:
下面是根据下面的标记请求 Chrome 中 headers 部分的屏幕截图。响应选项卡显示正确显示为 GeoJSON object 的数据。我还将该响应复制到本地文件中,并将其用作地图源并取得了积极成果。
到目前为止的数据测试:GeoJSON 文件 (2.1mb)
- 本地文件,本地服务器:成功(响应时间 54ms)
- 相同的文件,远程服务器:数据返回浏览器之前出现 D3 错误(750 毫秒)
- API 来自远程服务器的调用:数据返回浏览器之前出现 D3 错误(2.1 秒)
这更像是 "band-aid",但请尝试将 getMapData
更改为:
getMapData() {
this._mapService.getMapData()
.subscribe(
mapData => {
if (mapData.features) {
this.setMap(mapData);
}
},
error => this.errorMessage = <any>error
)
}
这将防止在没有 mapData.features
的情况下调用 setMap
。
它不能使用 Promise 而不是 Observable 吗?像
为您服务:
getMapData (): Promise<any> {
return this._http.get(this.url)
.toPromise()
.then(this.extractData)
.catch(this.handleError);
}
您也可以在此函数中直接提取数据,例如:
.then(response => response.json().data)
在您的组件中:
getMapData() {
this._mapService.getMapData()
.then(
mapData => mapData = this.setMap(mapData),
error => this.errorMessage = <any>error
)
}
我唯一关心的是在上面的代码中在哪里调用 setMap 函数。由于无法测试,希望对您有所帮助。
您是否尝试过将函数从构造函数移至 ngOnInit,例如:
import { Component, ElementRef, Input, OnInit } from '@angular/core';
import * as D3 from 'd3';
import '../rxjs-operators';
import { MapService } from '../map.service';
@Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements OnInit {
errorMessage: string;
height;
host;
htmlElement: HTMLElement;
mapData;
margin;
projection;
path;
svg;
width;
constructor (private _element: ElementRef, private _mapService: MapService) {}
setup() {
this.margin = {
top: 15,
right: 50,
bottom: 40,
left: 50
};
this.width = document.querySelector('#map').clientWidth - this.margin.left - this.margin.right;
this.height = this.width * 0.6 - this.margin.bottom - this.margin.top;
}
buildSVG() {
this.host.html('');
this.svg = this.host.append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
}
setMap(mapData) {
this.mapData = mapData;
this.projection = D3.geoAlbersUsa()
.translate([this.width /2 , this.height /2 ])
.scale(650);
this.path = D3.geoPath()
.projection(this.projection);
this.svg.selectAll('path')
.data(this.mapData.features)
.enter().append('path')
.attr('d', this.path)
.style('stroke', '#fff')
.style('stroke-width', '1')
.style('fill', 'lightgrey');
}
ngOnInit() {
this.host = D3.select(this._element.nativeElement);
this.setup();
this.buildSVG();
this._mapService.getMapData()
.subscribe(
mapData => this.setMap(mapData),
error => this.errorMessage = <any>error
)
}
}
现在,我不确定它会改变什么,但使用生命周期挂钩 (OnInit) 而不是构造函数被认为是一种很好的做法。参见 。
我的猜测是 angular 在构造函数和请求返回之间弄乱了对 map
元素的引用。我的建议是在服务器响应到达时开始在 ngAfterViewInit
内构建 svg
甚至更好。我认为这个问题主要是基于时机。当然,如果从服务器接收到的数据没有格式错误,您实际上可以在控制台中记录一组不错的映射数据。
此外,如果视图尚未准备好并且 #map
在 map.component.html
内,document.querySelector('#map').clientWidth
将 return 为 0 或未定义。
在模板中处理元素时,请始终使用 ngAfterViewInit
生命周期挂钩。
除此之外,您似乎没有在组件中使用任何 angular 的变更检测。我建议你,为了防止对你的元素有任何干扰,从 ChangeDetectorRef
:
中分离出来
@Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implement AfterViewInit {
private mapData;
constructor (
private _element: ElementRef,
private _mapService: MapService,
private _changeRef: ChangeDetectorRef
){}
ngAfterViewInit(): void {
this._changeRef.detach();
this.getMapData();
}
getMapData() {
this._mapService.getMapData().subscribe((mapData) => {
this.mapData = mapData;
this.setup();
this.buildSvg();
this.setMapData();
});
}
setup() {
//...
}
buildSVG() {
//...
}
setMapData(mapData) {
//...
}
}
附录
另一方面,在分析您的步骤时:
- 你创建了一个 svg
- 附加一个
g
到它
- 然后你做一个
selectAll('path')
- 并尝试将数据添加到此选择
- 并且只有在那之后你才尝试附加一个
path
您可以尝试先附加路径,然后再向其中添加数据吗?或者使用
this.svg.selectAll('g')
对我来说更有意义,或者我可能真的不明白 selectAll
是如何工作的。
第二个附录
我想我现在真的为你明白了 :D 你能把你的 extractData
功能改成这样吗:
private extractData(res: Response) {
return res.json()
}
我的猜测是您的网络服务器不会 return 具有数据 属性 的对象中的地图数据,而是立即获取对象,并且您的实现似乎直接来自 angular.io 食谱 :)
哇。这是一次旅行!
这里是 tl;dr - 我遇到了两个问题:返回数据的格式和数据延迟。
- 数据格式:当我的 json 文件在服务器上时,api 调用会将其包装在 { data: } 对象中,但当它从 api 提供时调用我的 clouodant 数据库,包装器不存在。 @PierreDuc,谢谢你。
- 我找到了解决延迟问题的 SO 答案 ->
这是修改后的代码和 tl 部分:
map.component.ts:
import { Component, ElementRef, Input, AfterViewInit, ChangeDetectorRef } from '@angular/core';
import * as d3 from 'd3/index';
import '../rxjs-operators';
import { MapService } from '../shared/map.service';
@Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements AfterViewInit {
errorMessage: string;
height;
host;
htmlElement: HTMLElement;
mapData;
margin;
projection;
path;
svg;
width;
constructor (
private _element: ElementRef,
private _mapService: MapService,
private _changeRef: ChangeDetectorRef
) { }
ngAfterViewInit(): void {
this._changeRef.detach();
this.getMapData();
}
getMapData() {
this._mapService.getMapData().subscribe(mapData => this.mapData = mapData, err => {}, () => this.setMap(this.mapData));
this.host = d3.select(this._element.nativeElement);
this.setup();
this.buildSVG();
}
setup() {
console.log('In setup()')
this.margin = {
top: 15,
right: 50,
bottom: 40,
left: 50
};
this.width = document.querySelector('#map').clientWidth - this.margin.left - this.margin.right;
this.height = this.width * 0.6 - this.margin.bottom - this.margin.top;
}
buildSVG() {
console.log('In buildSVG()');
this.host.html('');
this.svg = this.host.append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
}
setMap(mapData) {
console.log('In setMap(mapData), mapData getting assigned');
this.mapData = mapData;
console.log('mapData assigned as ' + this.mapData);
this.projection = d3.geoAlbersUsa()
.translate([this.width /2 , this.height /2 ])
.scale(650);
this.path = d3.geoPath()
.projection(this.projection);
this.svg.selectAll('path')
.data(this.mapData.features)
.enter().append('path')
.attr('d', this.path)
.style('stroke', '#fff')
.style('stroke-width', '1')
.style('fill', 'lightgrey');
}
}
map.service.ts:
import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class MapService {
// private url = 'http://localhost:3000/mapData'; // TopoJSON file on the server (5.6 ms)
// private url = 'http://localhost:3000/mapDataAPI'; // GeoJSON file on the server (54 ms)
// private url = 'http://localhost:3000/api/mapData'; // get json data from a local server connecting to cloudant for the data (750ms)
private url = 'https://???.mybluemix.net/api/mapData'; // get GeoJSON from the cloud-side server api getting data from cloudant (1974 ms per Postman)
constructor (private _http: Http) { }
getMapData(): Observable<any> {
return this._http.get(this.url)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body; // the data returned from cloudant doesn't get wrapped in a { data: } object
// return body.data; // this works for files served from the server that get wrapped in a { data: } object
}
private handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg);
return Promise.reject(errMsg);
}
}
我非常感谢大家的意见 - 我仍然需要对代码进行清理 - 可能还有一些事情要做,但数据创建了地图。我的下一个任务是添加数据和动画。我正在拍摄与此类似的演示文稿:http://ww2.kqed.org/lowdown/2015/09/21/now-that-summers-over-what-do-californias-reservoirs-look-like-a-real-time-visualization/
您可以在这里找到它的代码:https://github.com/vicapow/water-supply
这很奇怪。它也有点长,所以提前道歉。 更新 - 结果是 2 个问题,请参阅下面的回答。
这是我的错误:EXCEPTION: this.svg.selectAll(...).data(...).enter is not a function
我有一个 angular-cli 客户端和一个节点 api 服务器。我可以使用可观察对象(下面的代码)从服务中检索 states.json 文件。 d3 喜欢该文件并显示预期的美国地图。
当我将 api 服务器中的服务目标从文件更改为 bluemix-cloudant 服务器时,我的客户端出现了上述错误。
当我 console.log 使用 ngOnInit 的变体中的输出时,最初 mapData 打印为空数组并抛出错误。 这是明显的来源错误,因为没有数据,但 Chrome 调试器显示 get 请求挂起。请求完成后,数据将按预期打印在控制台中。
- angular-cli 版本 1.0.0-beta.26
- angular版本^2.3.1
- d3 版本 ^4.4.4
- rxjs 版本 ^5.0.1
map.component.ts:
import { Component, ElementRef, Input } from '@angular/core';
import * as D3 from 'd3';
import '../rxjs-operators';
import { MapService } from '../map.service';
@Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent {
errorMessage: string;
height;
host;
htmlElement: HTMLElement;
mapData;
margin;
projection;
path;
svg;
width;
constructor (private _element: ElementRef, private _mapService: MapService) {
this.host = D3.select(this._element.nativeElement);
this.getMapData();
this.setup();
this.buildSVG();
}
getMapData() {
this._mapService.getMapData()
.subscribe(
mapData => this.setMap(mapData),
error => this.errorMessage = <any>error
)
}
setup() {
this.margin = {
top: 15,
right: 50,
bottom: 40,
left: 50
};
this.width = document.querySelector('#map').clientWidth - this.margin.left - this.margin.right;
this.height = this.width * 0.6 - this.margin.bottom - this.margin.top;
}
buildSVG() {
this.host.html('');
this.svg = this.host.append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
}
setMap(mapData) {
this.mapData = mapData;
this.projection = D3.geoAlbersUsa()
.translate([this.width /2 , this.height /2 ])
.scale(650);
this.path = D3.geoPath()
.projection(this.projection);
this.svg.selectAll('path')
.data(this.mapData.features)
.enter().append('path')
.attr('d', this.path)
.style('stroke', '#fff')
.style('stroke-width', '1')
.style('fill', 'lightgrey');
}
}
map.service.ts:
import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class MapService {
private url = 'http://localhost:3000/api/mapData';
private socket;
constructor (private _http: Http) { }
getMapData(): Observable<any> {
return this._http.get(this.url)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || {};
}
private handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg);
return Promise.reject(errMsg);
}
}
这个函数是异步的,d3 调用数据的时间太长了吗?
我本来希望这个问题
非常感谢任何帮助或见解!
编辑: 下面是根据下面的标记请求 Chrome 中 headers 部分的屏幕截图。响应选项卡显示正确显示为 GeoJSON object 的数据。我还将该响应复制到本地文件中,并将其用作地图源并取得了积极成果。
到目前为止的数据测试:GeoJSON 文件 (2.1mb)
- 本地文件,本地服务器:成功(响应时间 54ms)
- 相同的文件,远程服务器:数据返回浏览器之前出现 D3 错误(750 毫秒)
- API 来自远程服务器的调用:数据返回浏览器之前出现 D3 错误(2.1 秒)
这更像是 "band-aid",但请尝试将 getMapData
更改为:
getMapData() {
this._mapService.getMapData()
.subscribe(
mapData => {
if (mapData.features) {
this.setMap(mapData);
}
},
error => this.errorMessage = <any>error
)
}
这将防止在没有 mapData.features
的情况下调用 setMap
。
它不能使用 Promise 而不是 Observable 吗?像
为您服务:
getMapData (): Promise<any> {
return this._http.get(this.url)
.toPromise()
.then(this.extractData)
.catch(this.handleError);
}
您也可以在此函数中直接提取数据,例如:
.then(response => response.json().data)
在您的组件中:
getMapData() {
this._mapService.getMapData()
.then(
mapData => mapData = this.setMap(mapData),
error => this.errorMessage = <any>error
)
}
我唯一关心的是在上面的代码中在哪里调用 setMap 函数。由于无法测试,希望对您有所帮助。
您是否尝试过将函数从构造函数移至 ngOnInit,例如:
import { Component, ElementRef, Input, OnInit } from '@angular/core';
import * as D3 from 'd3';
import '../rxjs-operators';
import { MapService } from '../map.service';
@Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements OnInit {
errorMessage: string;
height;
host;
htmlElement: HTMLElement;
mapData;
margin;
projection;
path;
svg;
width;
constructor (private _element: ElementRef, private _mapService: MapService) {}
setup() {
this.margin = {
top: 15,
right: 50,
bottom: 40,
left: 50
};
this.width = document.querySelector('#map').clientWidth - this.margin.left - this.margin.right;
this.height = this.width * 0.6 - this.margin.bottom - this.margin.top;
}
buildSVG() {
this.host.html('');
this.svg = this.host.append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
}
setMap(mapData) {
this.mapData = mapData;
this.projection = D3.geoAlbersUsa()
.translate([this.width /2 , this.height /2 ])
.scale(650);
this.path = D3.geoPath()
.projection(this.projection);
this.svg.selectAll('path')
.data(this.mapData.features)
.enter().append('path')
.attr('d', this.path)
.style('stroke', '#fff')
.style('stroke-width', '1')
.style('fill', 'lightgrey');
}
ngOnInit() {
this.host = D3.select(this._element.nativeElement);
this.setup();
this.buildSVG();
this._mapService.getMapData()
.subscribe(
mapData => this.setMap(mapData),
error => this.errorMessage = <any>error
)
}
}
现在,我不确定它会改变什么,但使用生命周期挂钩 (OnInit) 而不是构造函数被认为是一种很好的做法。参见
我的猜测是 angular 在构造函数和请求返回之间弄乱了对 map
元素的引用。我的建议是在服务器响应到达时开始在 ngAfterViewInit
内构建 svg
甚至更好。我认为这个问题主要是基于时机。当然,如果从服务器接收到的数据没有格式错误,您实际上可以在控制台中记录一组不错的映射数据。
此外,如果视图尚未准备好并且 #map
在 map.component.html
内,document.querySelector('#map').clientWidth
将 return 为 0 或未定义。
在模板中处理元素时,请始终使用 ngAfterViewInit
生命周期挂钩。
除此之外,您似乎没有在组件中使用任何 angular 的变更检测。我建议你,为了防止对你的元素有任何干扰,从 ChangeDetectorRef
:
@Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implement AfterViewInit {
private mapData;
constructor (
private _element: ElementRef,
private _mapService: MapService,
private _changeRef: ChangeDetectorRef
){}
ngAfterViewInit(): void {
this._changeRef.detach();
this.getMapData();
}
getMapData() {
this._mapService.getMapData().subscribe((mapData) => {
this.mapData = mapData;
this.setup();
this.buildSvg();
this.setMapData();
});
}
setup() {
//...
}
buildSVG() {
//...
}
setMapData(mapData) {
//...
}
}
附录
另一方面,在分析您的步骤时:
- 你创建了一个 svg
- 附加一个
g
到它 - 然后你做一个
selectAll('path')
- 并尝试将数据添加到此选择
- 并且只有在那之后你才尝试附加一个
path
您可以尝试先附加路径,然后再向其中添加数据吗?或者使用
this.svg.selectAll('g')
对我来说更有意义,或者我可能真的不明白 selectAll
是如何工作的。
第二个附录
我想我现在真的为你明白了 :D 你能把你的 extractData
功能改成这样吗:
private extractData(res: Response) {
return res.json()
}
我的猜测是您的网络服务器不会 return 具有数据 属性 的对象中的地图数据,而是立即获取对象,并且您的实现似乎直接来自 angular.io 食谱 :)
哇。这是一次旅行!
这里是 tl;dr - 我遇到了两个问题:返回数据的格式和数据延迟。
- 数据格式:当我的 json 文件在服务器上时,api 调用会将其包装在 { data: } 对象中,但当它从 api 提供时调用我的 clouodant 数据库,包装器不存在。 @PierreDuc,谢谢你。
- 我找到了解决延迟问题的 SO 答案 ->
这是修改后的代码和 tl 部分:
map.component.ts:
import { Component, ElementRef, Input, AfterViewInit, ChangeDetectorRef } from '@angular/core';
import * as d3 from 'd3/index';
import '../rxjs-operators';
import { MapService } from '../shared/map.service';
@Component({
selector: 'map-component',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent implements AfterViewInit {
errorMessage: string;
height;
host;
htmlElement: HTMLElement;
mapData;
margin;
projection;
path;
svg;
width;
constructor (
private _element: ElementRef,
private _mapService: MapService,
private _changeRef: ChangeDetectorRef
) { }
ngAfterViewInit(): void {
this._changeRef.detach();
this.getMapData();
}
getMapData() {
this._mapService.getMapData().subscribe(mapData => this.mapData = mapData, err => {}, () => this.setMap(this.mapData));
this.host = d3.select(this._element.nativeElement);
this.setup();
this.buildSVG();
}
setup() {
console.log('In setup()')
this.margin = {
top: 15,
right: 50,
bottom: 40,
left: 50
};
this.width = document.querySelector('#map').clientWidth - this.margin.left - this.margin.right;
this.height = this.width * 0.6 - this.margin.bottom - this.margin.top;
}
buildSVG() {
console.log('In buildSVG()');
this.host.html('');
this.svg = this.host.append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
}
setMap(mapData) {
console.log('In setMap(mapData), mapData getting assigned');
this.mapData = mapData;
console.log('mapData assigned as ' + this.mapData);
this.projection = d3.geoAlbersUsa()
.translate([this.width /2 , this.height /2 ])
.scale(650);
this.path = d3.geoPath()
.projection(this.projection);
this.svg.selectAll('path')
.data(this.mapData.features)
.enter().append('path')
.attr('d', this.path)
.style('stroke', '#fff')
.style('stroke-width', '1')
.style('fill', 'lightgrey');
}
}
map.service.ts:
import { Http, Response } from '@angular/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class MapService {
// private url = 'http://localhost:3000/mapData'; // TopoJSON file on the server (5.6 ms)
// private url = 'http://localhost:3000/mapDataAPI'; // GeoJSON file on the server (54 ms)
// private url = 'http://localhost:3000/api/mapData'; // get json data from a local server connecting to cloudant for the data (750ms)
private url = 'https://???.mybluemix.net/api/mapData'; // get GeoJSON from the cloud-side server api getting data from cloudant (1974 ms per Postman)
constructor (private _http: Http) { }
getMapData(): Observable<any> {
return this._http.get(this.url)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body; // the data returned from cloudant doesn't get wrapped in a { data: } object
// return body.data; // this works for files served from the server that get wrapped in a { data: } object
}
private handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg);
return Promise.reject(errMsg);
}
}
我非常感谢大家的意见 - 我仍然需要对代码进行清理 - 可能还有一些事情要做,但数据创建了地图。我的下一个任务是添加数据和动画。我正在拍摄与此类似的演示文稿:http://ww2.kqed.org/lowdown/2015/09/21/now-that-summers-over-what-do-californias-reservoirs-look-like-a-real-time-visualization/
您可以在这里找到它的代码:https://github.com/vicapow/water-supply