diff --git a/src/app/cartography/components/d3-map/d3-map.component.html b/src/app/cartography/components/d3-map/d3-map.component.html index ffa2be61..dd7a3108 100644 --- a/src/app/cartography/components/d3-map/d3-map.component.html +++ b/src/app/cartography/components/d3-map/d3-map.component.html @@ -1,5 +1,19 @@ + + + + + + + + + + + + + + diff --git a/src/app/cartography/components/d3-map/d3-map.component.ts b/src/app/cartography/components/d3-map/d3-map.component.ts index 07c2673e..98034021 100644 --- a/src/app/cartography/components/d3-map/d3-map.component.ts +++ b/src/app/cartography/components/d3-map/d3-map.component.ts @@ -32,6 +32,7 @@ import { Server } from '../../../models/server'; import { ToolsService } from '../../../services/tools.service'; import { TextEditorComponent } from '../text-editor/text-editor.component'; import { MapScaleService } from '../../../services/mapScale.service'; +import { Project } from '../../../models/project'; @Component({ selector: 'app-d3-map', @@ -43,6 +44,7 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy { @Input() links: Link[] = []; @Input() drawings: Drawing[] = []; @Input() symbols: Symbol[] = []; + @Input() project: Project; @Input() server: Server; @Input() width = 1500; @@ -53,15 +55,13 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy { private parentNativeElement: any; private svg: Selection; - private onChangesDetected: Subscription; private subscriptions: Subscription[] = []; - private drawLinkTool: boolean; - protected settings = { show_interface_labels: true }; + public gridVisibility: number = 0; constructor( private graphDataManager: GraphDataManager, @@ -143,6 +143,8 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy { this.drawLinkTool = value; }) ); + + this.gridVisibility = localStorage.getItem('gridVisibility') === 'true' ? 1 : 0; } ngOnDestroy() { diff --git a/src/app/cartography/converters/map/map-node-to-node-converter.ts b/src/app/cartography/converters/map/map-node-to-node-converter.ts index 6c5fbac1..206fdc07 100644 --- a/src/app/cartography/converters/map/map-node-to-node-converter.ts +++ b/src/app/cartography/converters/map/map-node-to-node-converter.ts @@ -31,6 +31,7 @@ export class MapNodeToNodeConverter implements Converter { node.status = mapNode.status; node.symbol = mapNode.symbol; node.symbol_url = mapNode.symbolUrl; + node.usage = mapNode.usage; node.width = mapNode.width; node.x = mapNode.x; node.y = mapNode.y; diff --git a/src/app/cartography/converters/map/node-to-map-node-converter.ts b/src/app/cartography/converters/map/node-to-map-node-converter.ts index b3e64b1d..b25c4111 100644 --- a/src/app/cartography/converters/map/node-to-map-node-converter.ts +++ b/src/app/cartography/converters/map/node-to-map-node-converter.ts @@ -40,6 +40,7 @@ export class NodeToMapNodeConverter implements Converter { mapNode.status = node.status; mapNode.symbol = node.symbol; mapNode.symbolUrl = node.symbol_url; + mapNode.usage = node.usage; mapNode.width = node.width; mapNode.x = node.x; mapNode.y = node.y; diff --git a/src/app/cartography/models/map/map-node.ts b/src/app/cartography/models/map/map-node.ts index fda84783..9f3e9749 100644 --- a/src/app/cartography/models/map/map-node.ts +++ b/src/app/cartography/models/map/map-node.ts @@ -23,6 +23,7 @@ export class MapNode implements Indexed { status: string; symbol: string; symbolUrl: string; + usage?: string; width: number; x: number; y: number; diff --git a/src/app/cartography/models/node.ts b/src/app/cartography/models/node.ts index c5960ae2..f9f41428 100644 --- a/src/app/cartography/models/node.ts +++ b/src/app/cartography/models/node.ts @@ -82,6 +82,7 @@ export class Node { status: string; symbol: string; symbol_url: string; // @TODO: full URL to symbol, move to MapNode once converters are moved to app module + usage?: string; width: number; x: number; y: number; diff --git a/src/app/components/drawings-listeners/drawing-dragged/drawing-dragged.component.ts b/src/app/components/drawings-listeners/drawing-dragged/drawing-dragged.component.ts index 14dccf64..2050a031 100644 --- a/src/app/components/drawings-listeners/drawing-dragged/drawing-dragged.component.ts +++ b/src/app/components/drawings-listeners/drawing-dragged/drawing-dragged.component.ts @@ -7,6 +7,7 @@ import { DraggedDataEvent } from '../../../cartography/events/event-source'; import { MapDrawing } from '../../../cartography/models/map/map-drawing'; import { Drawing } from '../../../cartography/models/drawing'; import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source'; +import { Project } from '../../../models/project'; @Component({ selector: 'app-drawing-dragged', @@ -15,6 +16,7 @@ import { DrawingsEventSource } from '../../../cartography/events/drawings-event- }) export class DrawingDraggedComponent implements OnInit, OnDestroy { @Input() server: Server; + @Input() project: Project; private drawingDragged: Subscription; constructor( @@ -33,7 +35,7 @@ export class DrawingDraggedComponent implements OnInit, OnDestroy { drawing.y += draggedEvent.dy; this.drawingService - .updatePosition(this.server, drawing, drawing.x, drawing.y) + .updatePosition(this.server, this.project, drawing, drawing.x, drawing.y) .subscribe((serverDrawing: Drawing) => { this.drawingsDataSource.update(serverDrawing); }); diff --git a/src/app/components/drawings-listeners/node-dragged/node-dragged.component.ts b/src/app/components/drawings-listeners/node-dragged/node-dragged.component.ts index 8c976075..695c46c7 100644 --- a/src/app/components/drawings-listeners/node-dragged/node-dragged.component.ts +++ b/src/app/components/drawings-listeners/node-dragged/node-dragged.component.ts @@ -7,6 +7,7 @@ import { Server } from '../../../models/server'; import { NodesEventSource } from '../../../cartography/events/nodes-event-source'; import { MapNode } from '../../../cartography/models/map/map-node'; import { Subscription } from 'rxjs'; +import { Project } from '../../../models/project'; @Component({ selector: 'app-node-dragged', @@ -15,6 +16,7 @@ import { Subscription } from 'rxjs'; }) export class NodeDraggedComponent implements OnInit, OnDestroy { @Input() server: Server; + @Input() project: Project private nodeDragged: Subscription; constructor( @@ -32,7 +34,7 @@ export class NodeDraggedComponent implements OnInit, OnDestroy { node.x += draggedEvent.dx; node.y += draggedEvent.dy; - this.nodeService.updatePosition(this.server, node, node.x, node.y).subscribe((serverNode: Node) => { + this.nodeService.updatePosition(this.server, this.project, node, node.x, node.y).subscribe((serverNode: Node) => { this.nodesDataSource.update(serverNode); }); } diff --git a/src/app/components/project-map/info-dialog/info-dialog.component.html b/src/app/components/project-map/info-dialog/info-dialog.component.html index 529b00e4..517f5820 100644 --- a/src/app/components/project-map/info-dialog/info-dialog.component.html +++ b/src/app/components/project-map/info-dialog/info-dialog.component.html @@ -9,6 +9,11 @@ + +
+ {{usage}} +
+
{{commandLine}} diff --git a/src/app/components/project-map/info-dialog/info-dialog.component.ts b/src/app/components/project-map/info-dialog/info-dialog.component.ts index 11968f47..893919fe 100644 --- a/src/app/components/project-map/info-dialog/info-dialog.component.ts +++ b/src/app/components/project-map/info-dialog/info-dialog.component.ts @@ -13,6 +13,7 @@ export class InfoDialogComponent implements OnInit { @Input() server: Server; @Input() node: Node; infoList: string[] = []; + usage: string = ''; commandLine: string = ''; constructor( @@ -23,6 +24,7 @@ export class InfoDialogComponent implements OnInit { ngOnInit() { this.infoList = this.infoService.getInfoAboutNode(this.node, this.server); this.commandLine = this.infoService.getCommandLine(this.node); + this.usage = this.node.usage ? this.node.usage : `No usage information has been provided for this node.`; } onCloseClick() { diff --git a/src/app/components/project-map/project-map.component.html b/src/app/components/project-map/project-map.component.html index 3c5a1257..362dc68e 100644 --- a/src/app/components/project-map/project-map.component.html +++ b/src/app/components/project-map/project-map.component.html @@ -2,6 +2,7 @@ Show layers +
+ + Show grid +
+ + Snap to grid
@@ -154,11 +161,11 @@ - + - + diff --git a/src/app/components/project-map/project-map.component.spec.ts b/src/app/components/project-map/project-map.component.spec.ts index ef7f3009..f8c55011 100644 --- a/src/app/components/project-map/project-map.component.spec.ts +++ b/src/app/components/project-map/project-map.component.spec.ts @@ -144,7 +144,7 @@ export class MockedDrawingService { return of(drawing); } - updatePosition(_server: Server, _drawing: Drawing, _x: number, _y: number) { + updatePosition(_server: Server, _project: Project, _drawing: Drawing, _x: number, _y: number) { return of(this.drawing); } diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index fa92a4d1..f9ef338f 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -84,6 +84,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { public isInterfaceLabelVisible: boolean = false; public notificationsVisibility: boolean = false; public layersVisibility: boolean = false; + public gridVisibility: boolean = false; tools = { selection: true, @@ -240,6 +241,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.notificationsVisibility = localStorage.getItem('notificationsVisibility') === 'true' ? true : false; this.layersVisibility = localStorage.getItem('layersVisibility') === 'true' ? true : false; + this.gridVisibility = localStorage.getItem('gridVisibility') === 'true' ? true : false; this.addKeyboardListeners(); } @@ -492,6 +494,20 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.mapChild.applyMapSettingsChanges(); } + public toggleGrid(visible: boolean) { + this.gridVisibility = visible; + if (this.gridVisibility) { + localStorage.setItem('gridVisibility', 'true'); + } else { + localStorage.removeItem('gridVisibility'); + } + this.mapChild.gridVisibility = this.gridVisibility ? 1 : 0; + } + + public toggleSnapToGrid(enabled: boolean) { + this.project.snap_to_grid = enabled; + } + private showMessage(msg) { if (this.notificationsVisibility) { if (msg.type === 'error') this.toasterService.error(msg.message); diff --git a/src/app/services/drawing.service.spec.ts b/src/app/services/drawing.service.spec.ts index 2377de7f..c95a10ba 100644 --- a/src/app/services/drawing.service.spec.ts +++ b/src/app/services/drawing.service.spec.ts @@ -8,17 +8,20 @@ import { Drawing } from '../cartography/models/drawing'; import { getTestServer } from './testing'; import { DrawingService } from './drawing.service'; import { AppTestingModule } from '../testing/app-testing/app-testing.module'; +import { Project } from '../models/project'; +import { SvgToDrawingConverter } from '../cartography/helpers/svg-to-drawing-converter'; describe('DrawingService', () => { let httpClient: HttpClient; let httpTestingController: HttpTestingController; let httpServer: HttpServer; let server: Server; + let project: Project = new Project(); beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule, AppTestingModule], - providers: [HttpServer, DrawingService] + providers: [HttpServer, SvgToDrawingConverter, DrawingService] }); httpClient = TestBed.get(HttpClient); @@ -40,7 +43,7 @@ describe('DrawingService', () => { drawing.project_id = 'myproject'; drawing.drawing_id = 'id'; - service.updatePosition(server, drawing, 10, 20).subscribe(); + service.updatePosition(server, project, drawing, 10, 20).subscribe(); const req = httpTestingController.expectOne('http://127.0.0.1:3080/v2/projects/myproject/drawings/id'); expect(req.request.method).toEqual('PUT'); @@ -55,7 +58,7 @@ describe('DrawingService', () => { drawing.project_id = 'myproject'; drawing.drawing_id = 'id'; - service.updatePosition(server, drawing, 10.1, 20.6).subscribe(); + service.updatePosition(server, project, drawing, 10.1, 20.6).subscribe(); const req = httpTestingController.expectOne('http://127.0.0.1:3080/v2/projects/myproject/drawings/id'); expect(req.request.method).toEqual('PUT'); diff --git a/src/app/services/drawing.service.ts b/src/app/services/drawing.service.ts index 5542fbd9..9c71d620 100644 --- a/src/app/services/drawing.service.ts +++ b/src/app/services/drawing.service.ts @@ -5,10 +5,15 @@ import { Observable } from 'rxjs'; import 'rxjs/add/operator/map'; import { Server } from '../models/server'; import { HttpServer } from './http-server.service'; +import { Project } from '../models/project'; +import { SvgToDrawingConverter } from '../cartography/helpers/svg-to-drawing-converter'; @Injectable() export class DrawingService { - constructor(private httpServer: HttpServer) {} + constructor( + private httpServer: HttpServer, + private svgToDrawingConverter: SvgToDrawingConverter + ) {} add(server: Server, project_id: string, x: number, y: number, svg: string) { return this.httpServer.post(server, `/projects/${project_id}/drawings`, { @@ -29,10 +34,23 @@ export class DrawingService { }); } - updatePosition(server: Server, drawing: Drawing, x: number, y: number): Observable { + updatePosition(server: Server, project: Project, drawing: Drawing, x: number, y: number): Observable { + let xPosition: number = Math.round(x); + let yPosition: number = Math.round(y); + + if (project.snap_to_grid) { + drawing.element = this.svgToDrawingConverter.convert(drawing.svg); + + xPosition = Math.round((xPosition + drawing.element.width/2) / project.drawing_grid_size) * project.drawing_grid_size; + yPosition = Math.round((yPosition + drawing.element.width/2) / project.drawing_grid_size) * project.drawing_grid_size; + + xPosition = Math.round(xPosition - drawing.element.width/2); + yPosition = Math.round(yPosition - drawing.element.height/2); + } + return this.httpServer.put(server, `/projects/${drawing.project_id}/drawings/${drawing.drawing_id}`, { - x: Math.round(x), - y: Math.round(y) + x: xPosition, + y: yPosition }); } diff --git a/src/app/services/node.service.spec.ts b/src/app/services/node.service.spec.ts index 1687c7ab..44feb302 100644 --- a/src/app/services/node.service.spec.ts +++ b/src/app/services/node.service.spec.ts @@ -154,11 +154,14 @@ describe('NodeService', () => { })); it('should updatePosition of node', inject([NodeService], (service: NodeService) => { + let project = { + project_id: '1' + } as Project; const node = new Node(); node.project_id = 'myproject'; node.node_id = 'id'; - service.updatePosition(server, node, 10, 20).subscribe(); + service.updatePosition(server, project, node, 10, 20).subscribe(); const req = httpTestingController.expectOne('http://127.0.0.1:3080/v2/projects/myproject/nodes/id'); expect(req.request.method).toEqual('PUT'); @@ -169,11 +172,14 @@ describe('NodeService', () => { })); it('should updatePosition of node and round to integer', inject([NodeService], (service: NodeService) => { + let project = { + project_id: '1' + } as Project; const node = new Node(); node.project_id = 'myproject'; node.node_id = 'id'; - service.updatePosition(server, node, 10.1, 20.6).subscribe(); + service.updatePosition(server, project, node, 10.1, 20.6).subscribe(); const req = httpTestingController.expectOne('http://127.0.0.1:3080/v2/projects/myproject/nodes/id'); expect(req.request.method).toEqual('PUT'); diff --git a/src/app/services/node.service.ts b/src/app/services/node.service.ts index 8de3b672..ff1b2bb6 100644 --- a/src/app/services/node.service.ts +++ b/src/app/services/node.service.ts @@ -53,10 +53,21 @@ export class NodeService { }); } - updatePosition(server: Server, node: Node, x: number, y: number): Observable { + updatePosition(server: Server, project: Project, node: Node, x: number, y: number): Observable { + let xPosition: number = Math.round(x); + let yPosition: number = Math.round(y); + + if (project.snap_to_grid) { + xPosition = Math.round((xPosition + node.width/2) / project.grid_size) * project.grid_size; + yPosition = Math.round((yPosition + node.width/2) / project.grid_size) * project.grid_size; + + xPosition = Math.round(xPosition - node.width/2); + yPosition = Math.round(yPosition - node.height/2); + } + return this.httpServer.put(server, `/projects/${node.project_id}/nodes/${node.node_id}`, { - x: Math.round(x), - y: Math.round(y) + x: xPosition, + y: yPosition }); }