Skip to content
This repository has been archived by the owner on Jun 17, 2024. It is now read-only.

Commit

Permalink
Code comments + installation instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
stellasia committed Sep 27, 2019
1 parent 281c576 commit c4fb335
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 17 deletions.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
# neo-map

A React application to visualize nodes with geographic attributes on a map.
A Neo4J Desktop (React-based) application to visualize nodes with geographic attributes on a map.


## Add the app to Neo4jDesktop

### From tarball

1. Go to the repository [releases](https://github.com/stellasia/neomap/releases)
2. Download the `noemap-<version>.tar.gz`
3. Open neo4j desktop and go to "Graph Applications" view:

![](img/desktop_graphapp_install.png)

4. Drag and drop the tarball you downloaded earlier below "Install Graph Application"
5. Trust the application
6. The application is now available and you can add it to your projects:

![](img/desktop_graphapp_install_add.png)

7. Click install

![](img/desktop_graphapp_install_add_2.png)


## Want to contribute?

## WARNING

I am a data scientist, not a front-end developer. If someone with expertise with React wants to take a look and suggest improvements, that would be very welcome!


### Developper mode
Expand Down
Binary file added img/desktop_graphapp_add.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/desktop_graphapp_install.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/desktop_graphapp_install_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "neomap",
"name": "@stellasia/neomap",
"version": "0.2.0",
"author": "Estelle Scifo",
"private": false,
Expand Down Expand Up @@ -30,10 +30,13 @@
"dist/",
"package.json"
],
"publishConfig": {
"registry": "https://npm.pkg.github.com/"
},
"description": "A Neo4j Desktop application to visualize nodes with geographic attributes on a map.",
"main": "index.js",
"devDependencies": {},
"repository": "git+https://github.com/stellasia/neomap.git",
"repository": "git://github.com/stellasia/neomap.git",
"icons": [
{
"src": "icon.png",
Expand Down
10 changes: 0 additions & 10 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,16 @@ body {
overflow: visible;
}


.disabled {
color: grey;
}


.nav-tabs {
margin-bottom: 20px;
background-color: #aaa;
color: black;
}

.tab-content {
border-left: 1px solid white;
padding-left: 10px;
border-bottom: 1px solid white;
padding-bottom: 10px;
margin-bottom: 10px;
}

p.help {
margin: 2px;
font-style: italic;
Expand Down
13 changes: 12 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class App extends Component {
constructor(props) {
super(props);

/*Get connection to the active graph
TODO: what happens if the active graph changes?
*/
if (window.neo4jDesktopApi) {
neo4jDesktopApi.getContext().then((context) => {
for (let project of context.projects) {
Expand All @@ -38,7 +42,8 @@ class App extends Component {
this.driver = this.getDriver();
}

console.log(this.driver);
//console.log(this.driver);

this.state = {
layers: {},
};
Expand All @@ -49,6 +54,9 @@ class App extends Component {


getDriver() {
/*Get a default driver based on hard coded credential above
TODO: remove or make this configurable through env vars or...
*/
var uri = DEFAULT_DRIVER.uri;
var usr = DEFAULT_DRIVER.user;
var pwd = DEFAULT_DRIVER.password;
Expand All @@ -63,6 +71,9 @@ class App extends Component {


layersChanged(childData) {
/* Something changed in the layer definition,
need to update map
*/
this.setState({
layers: childData.layers
});
Expand Down
11 changes: 11 additions & 0 deletions src/components/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class Map extends Component {


renderMarker(d, j, icon) {
/*Render maker with optional tooltip
*/
if (d.tooltip) {
return (
<Marker key={j} position={d.pos} icon={icon} >
Expand All @@ -32,6 +34,8 @@ class Map extends Component {


renderMarkerLayer(layer) {
/*Will show one marker per items in `layer.data`
*/
var data = layer.data;
var color = layer.color;
var url = `https://cdn.rawgit.com/pointhi/leaflet-color-markers/master/img/marker-icon-${color}.png`;
Expand All @@ -53,6 +57,8 @@ class Map extends Component {


renderHeatmapLayer(layer) {
/* Create heatmap based on items in `layer.data`, each with weight 1.
*/
var data = layer.data;
return (
<LayersControl.Overlay key={layer.ukey} name={layer.name} checked>
Expand All @@ -71,13 +77,18 @@ class Map extends Component {


renderClusterLayer(layer) {
/*TODO: cluster layer
*/
return "Cluster layer not supported for now";
};


render() {
var layers = Object.entries(this.props.layers);

/*Map center will be the one of the last layer...
TODO: compute zoom or use leaflet tools to set it automatically?
*/
var center ;
var zoom ;
if (layers.length > 0) {
Expand Down
3 changes: 3 additions & 0 deletions src/components/SideBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ class SideBar extends Component {


sendData(layers) {
/*Receives data from child layer
and propagete it to parent
*/
this.setState({
layers: layers.layers
});
Expand Down
36 changes: 33 additions & 3 deletions src/components/layers/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ import Accordion from 'react-bootstrap/Accordion';
import Card from 'react-bootstrap/Card';
import { CypherEditor } from "graph-app-kit/components/Editor"

// css needed for CypherEditor
import "codemirror/lib/codemirror.css";
import "codemirror/addon/lint/lint.css";
import "codemirror/addon/hint/show-hint.css";
import "cypher-codemirror/dist/cypher-codemirror-syntax.css";



// maximum number of points to show
const LIMIT = 500;

// marker colors
const POSSIBLE_COLORS = [
{value: "blue", label: "Blue"},
{value: "red", label: "Red"},
Expand All @@ -29,10 +31,12 @@ const POSSIBLE_COLORS = [
];


// layer type: either from node labels or cypher
const LAYER_TYPE_LATLON = "latlon";
const LAYER_TYPE_CYPHER = "cypher";


// default parameters for new layers
const DEFAULT_LAYER = {
name: "New layer",
layerType: LAYER_TYPE_LATLON,
Expand Down Expand Up @@ -65,8 +69,9 @@ class Layer extends Component {

this.driver = props.driver;

// list of available nodes
this.nodes = this.getNodes();

this.sendData = this.sendData.bind(this);
this.deleteLayer = this.deleteLayer.bind(this);
this.handleNameChange = this.handleNameChange.bind(this);
Expand All @@ -84,6 +89,8 @@ class Layer extends Component {


updatePosition() {
/*Set the map center based on `this.state.data`
*/
var arr = this.state.data;
var pos = [47, 3];
if (arr.length > 0) {
Expand Down Expand Up @@ -116,13 +123,17 @@ class Layer extends Component {


getQuery() {
/*If layerType==cypher, query is inside the CypherEditor,
otherwise, we need to build the query manually.
*/
if (this.state.layerType === LAYER_TYPE_CYPHER)
return this.getCypherQuery();

// lat lon query
// TODO: improve this method...
var query = "";
query = 'MATCH (n) WHERE true';
// filter wanted node labels
if (this.state.nodeLabel.length > 0) {
var sub_q = "(false";
this.state.nodeLabel.forEach( (value, key) => {
Expand All @@ -132,18 +143,26 @@ class Layer extends Component {
sub_q += ")";
query += " AND " + sub_q;
}
// filter out nodes with null latitude or longitude
query += ` AND n.${this.state.latitudeProperty} IS NOT NULL AND n.${this.state.longitudeProperty} IS NOT NULL`;
// return latitude, longitude
query += ` RETURN n.${this.state.latitudeProperty} as latitude, n.${this.state.longitudeProperty} as longitude`;

// if tooltip is not null, also return tooltip
if (this.state.tooltipProperty !== undefined)
query += `, n.${this.state.tooltipProperty} as tooltip`;

// TODO: is that really needed???
// limit the number of points to avoid browser crash...
query += ` LIMIT ${this.state.limit}`;

return query;
};


updateData() {
/*Query database and update `this.state.data`
*/
var res = [];
const session = this.driver.session();

Expand Down Expand Up @@ -226,12 +245,16 @@ class Layer extends Component {


sendData(event) {
/*Send data to parent which will propagate to the Map component
*/
this.updateData();
event.preventDefault();
};


deleteLayer(event) {
/*Remove the layer
*/
this.props.deleteLayer(this.state.ukey);
event.preventDefault();
};
Expand All @@ -240,6 +263,8 @@ class Layer extends Component {
getNodes() {
/*This will be updated quite often,
is that what we want?
TODO: use apoc procedure for that, the query below can be quite loong...
*/
if (this.driver === undefined)
return [];
Expand Down Expand Up @@ -268,12 +293,14 @@ class Layer extends Component {


renderConfigCypher() {
/*If layerType==cypher, then we display the CypherEditor
*/
if (this.state.layerType !== LAYER_TYPE_CYPHER)
return ""
return (
<div className="form-group">
<h5>Query</h5>
<p className="help">Checkout <a href="https://github.com/stellasia/neomap/wiki" target="_blank">the documentation</a></p>
<p className="help">Checkout <a href="https://github.com/stellasia/neomap/wiki" target="_blank" rel="noopener noreferrer" >the documentation</a></p>
<p className="help">(Ctrl+SPACE for autocomplete)</p>
<CypherEditor
value={this.state.cypher}
Expand All @@ -285,6 +312,9 @@ class Layer extends Component {


renderConfigDefault() {
/*If layerType==latlon, then we display the elements to choose
node labels and properties to be used.
*/
if (this.state.layerType !== LAYER_TYPE_LATLON)
return ""
return (
Expand Down
7 changes: 7 additions & 0 deletions src/components/layers/layers_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class LayersList extends Component {


sendData(data) {
/*Receives new data from child layer
and propagete it to parent
*/
var new_layer = data.layer;
var layers = this.state.layers;
layers[new_layer.ukey] = new_layer;
Expand All @@ -34,6 +37,10 @@ class LayersList extends Component {


deleteLayer(ukey) {
/*Remove a specific ukey from
`this.state.layers` map
and re-render map component
*/
var layers = this.state.layers;
delete layers[ukey];
this.setState({
Expand Down

0 comments on commit c4fb335

Please sign in to comment.