diff --git a/res/css/style.css b/res/css/style.css index 60e570cdfeaf4bcce2f0de9b02896e2d0566d8da..f30d0529f2d7c8f2af1c551f27ae4ef6a9a47b1f 100644 --- a/res/css/style.css +++ b/res/css/style.css @@ -12,6 +12,10 @@ -fx-background-radius: 0.3em; } +.choice-box .arrow{ + -fx-background-color: white; +} + .choice-box > .label { -fx-text-fill: white; -fx-font-weight: bold; @@ -26,3 +30,16 @@ -fx-background-color: #1aa6a4; -fx-text-fill: white; } + +.spinner .increment-arrow-button, +.spinner .decrement-arrow-button { + -fx-background-color: #116B69; +} + +.spinner .increment-arrow-button .increment-arrow { + -fx-background-color: white; +} + +.spinner .decrement-arrow-button .decrement-arrow { + -fx-background-color: white; +} \ No newline at end of file diff --git a/res/stages/axes-settings-stage.fxml b/res/stages/axes-settings-stage.fxml index a02d3c2546e0207375d98888582f3b72699ef3da..e19aa3020780237261a7602394e6b241b1913e23 100644 --- a/res/stages/axes-settings-stage.fxml +++ b/res/stages/axes-settings-stage.fxml @@ -9,31 +9,88 @@ <Stage fx:id="stage" xmlns="http://javafx.com/javafx/17.0.12" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fr.univlille.sae.classification.controller.AxesSettingsController"> <scene> <Scene> - <AnchorPane prefHeight="200" prefWidth="200"> + <AnchorPane prefHeight="226.0" prefWidth="440.0"> <children> - <VBox alignment="CENTER" prefHeight="200.0" prefWidth="406.0" spacing="15.0"> - <children> - <HBox alignment="CENTER" prefHeight="46.0" prefWidth="406.0" spacing="20.0"> - <children> - <Label text="Valeur des ordonnées"> - <font> - <Font name="System Bold" size="13.0" /> - </font></Label> - <ChoiceBox fx:id="selectOrd" prefWidth="150.0" stylesheets="@../css/style.css" /> - </children> - </HBox> - <HBox alignment="CENTER" prefHeight="44.0" prefWidth="406.0" spacing="20.0"> - <children> - <Label text="Valeur des abscisses"> - <font> - <Font name="System Bold" size="13.0" /> - </font></Label> - <ChoiceBox fx:id="selectAbs" prefWidth="150.0" stylesheets="@../css/style.css" /> - </children> - </HBox> - <Button fx:id="confirmAxes" mnemonicParsing="false" onAction="#validate" stylesheets="@../css/style.css" text="Valider" textFill="WHITE" /> - </children> - </VBox> + <TabPane prefHeight="226.0" prefWidth="455.0" tabClosingPolicy="UNAVAILABLE"> + <tabs> + <Tab text="Valeur des Axes"> + <content> + <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"> + <children> + <VBox alignment="CENTER" prefHeight="195.0" prefWidth="457.0" spacing="15.0"> + <children> + <HBox alignment="CENTER" prefHeight="46.0" prefWidth="406.0" spacing="20.0"> + <children> + <Label text="Valeur des ordonnées"> + <font> + <Font name="System Bold" size="13.0" /> + </font> + </Label> + <ChoiceBox fx:id="selectOrd" prefWidth="150.0" stylesheets="@../css/style.css" /> + </children> + </HBox> + <HBox alignment="CENTER" prefHeight="44.0" prefWidth="406.0" spacing="20.0"> + <children> + <Label text="Valeur des abscisses"> + <font> + <Font name="System Bold" size="13.0" /> + </font> + </Label> + <ChoiceBox fx:id="selectAbs" prefWidth="150.0" stylesheets="@../css/style.css" /> + </children> + </HBox> + <Button fx:id="confirmAxes" mnemonicParsing="false" onAction="#validate" stylesheets="@../css/style.css" text="Valider" textFill="WHITE" /> + </children> + </VBox> + </children></AnchorPane> + </content> + </Tab> + <Tab text="Dimension des axes"> + <content> + <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0"> + <children> + <VBox alignment="CENTER" prefHeight="195.0" prefWidth="457.0" spacing="15.0"> + <children> + <HBox alignment="CENTER" prefHeight="46.0" prefWidth="406.0" spacing="20.0"> + <children> + <Label text="Borne des ordonnées"> + <font> + <Font name="System Bold" size="13.0" /> + </font> + </Label> + <Spinner fx:id="OrdSizeLower" editable="true" prefHeight="26.0" prefWidth="107.0" stylesheets="@../css/style.css" /> + <Spinner fx:id="OrdSizeUpper" editable="true" layoutX="264.0" layoutY="20.0" prefHeight="26.0" prefWidth="107.0" stylesheets="@../css/style.css" /> + </children> + </HBox> + <HBox alignment="CENTER" prefHeight="44.0" prefWidth="406.0" spacing="20.0"> + <children> + <Label text="Borne des abscisses"> + <font> + <Font name="System Bold" size="13.0" /> + </font> + </Label> + <Spinner fx:id="AbsSizeLower" editable="true" prefHeight="26.0" prefWidth="104.0" stylesheets="@../css/style.css" /> + <Spinner fx:id="AbsSizeUpper" editable="true" layoutX="261.0" layoutY="19.0" prefHeight="26.0" prefWidth="104.0" stylesheets="@../css/style.css" /> + </children> + </HBox> + <HBox alignment="CENTER" prefHeight="21.0" prefWidth="457.0" spacing="20.0"> + <children> + <Button fx:id="confirmAxes" mnemonicParsing="false" onAction="#validate" stylesheets="@../css/style.css" text="Valider" textFill="WHITE" /> + <Button fx:id="reset" layoutX="208.0" layoutY="10.0" mnemonicParsing="false" onAction="#reset" stylesheets="@../css/style.css" text="Rénitialiser" textFill="WHITE" /> + </children> + </HBox> + </children> + </VBox> + </children></AnchorPane> + </content> + </Tab> + <Tab text="Couleurs des données"> + <content> + <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" /> + </content> + </Tab> + </tabs> + </TabPane> </children></AnchorPane> </Scene> </scene> diff --git a/res/stages/k-NN-stage.fxml b/res/stages/k-NN-stage.fxml new file mode 100644 index 0000000000000000000000000000000000000000..d85e37816d2a07190893f8454bbc57f4dc5c0871 --- /dev/null +++ b/res/stages/k-NN-stage.fxml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.*?> +<?import javafx.scene.*?> +<?import javafx.scene.control.*?> +<?import javafx.scene.layout.*?> +<?import javafx.scene.text.*?> +<?import javafx.stage.*?> + +<Stage fx:id="stage" xmlns="http://javafx.com/javafx/17.0.12" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fr.univlille.sae.classification.controller.KNNController"> + <scene> + <Scene> + <AnchorPane prefHeight="224.0" prefWidth="363.0"> + <children> + <VBox layoutX="-1.0" layoutY="-3.0" prefHeight="229.0" prefWidth="364.0"> + <children> + <HBox alignment="CENTER" prefHeight="92.0" prefWidth="364.0"> + <children> + <ChoiceBox fx:id="algoSelector" prefWidth="150.0" stylesheets="@../css/style.css" /> + </children> + <padding> + <Insets top="20.0" /> + </padding> + </HBox> + <HBox alignment="CENTER_LEFT" prefHeight="24.0" prefWidth="314.0"> + <children> + <Label text="Attribution de K"> + <font> + <Font name="System Bold" size="14.0" /> + </font> + </Label> + </children> + <VBox.margin> + <Insets left="50.0" /> + </VBox.margin> + </HBox> + <HBox alignment="CENTER" prefHeight="36.0" prefWidth="364.0" spacing="20.0"> + <children> + <Spinner fx:id="kEntry" /> + <Button fx:id="autoK" mnemonicParsing="false" onAction="#bestK" stylesheets="@../css/style.css" text="Attribution auto" textFill="WHITE"> + <font> + <Font name="System Bold" size="13.0" /> + </font> + </Button> + </children> + </HBox> + <HBox alignment="CENTER" prefHeight="59.0" prefWidth="364.0"> + <children> + <Button fx:id="confirmK" onAction="#validate" mnemonicParsing="false" stylesheets="@../css/style.css" text="Valider" textFill="WHITE"> + <font> + <Font name="System Bold" size="14.0" /> + </font></Button> + </children> + </HBox> + </children> + </VBox> + </children></AnchorPane> + </Scene> + </scene> +</Stage> diff --git a/res/stages/load-data-stage.fxml b/res/stages/load-data-stage.fxml index f0766b0d8d0f41b8a18d351c3cded574a3dffe43..6c6f3fc9506adec0621557c674947a76c68e0ef8 100644 --- a/res/stages/load-data-stage.fxml +++ b/res/stages/load-data-stage.fxml @@ -22,6 +22,11 @@ <TextField fx:id="filePath" prefHeight="26.0" prefWidth="207.0" /> </children> </HBox> + <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0"> + <children> + <ChoiceBox fx:id="fileType" prefWidth="150.0" stylesheets="@../css/style.css" /> + </children> + </HBox> <HBox alignment="CENTER" prefHeight="76.0" prefWidth="310.0"> <children> <Button fx:id="confirmDataSelection" mnemonicParsing="false" onAction="#validate" stylesheets="@../css/style.css" text="Valider" textFill="WHITE" /> diff --git a/res/stages/main-stage.fxml b/res/stages/main-stage.fxml index 3e0b0232d8ab29e8a875ec891a514621d5c10162..e867ac83924ee95ffef3b2b01e4cf2d69183734f 100644 --- a/res/stages/main-stage.fxml +++ b/res/stages/main-stage.fxml @@ -29,7 +29,7 @@ <children> <AnchorPane prefHeight="426.0" prefWidth="698.0"> <children> - <ScatterChart fx:id="scatterChart" layoutX="-1.0" legendVisible="false" prefHeight="426.0" prefWidth="677.0" stylesheets="@../css/style.css"> + <ScatterChart fx:id="scatterChart" layoutX="-1.0" legendVisible="false" prefHeight="345.0" prefWidth="677.0" stylesheets="@../css/style.css"> <xAxis> <NumberAxis fx:id="absAxe" prefHeight="42.0" prefWidth="617.0" side="BOTTOM" stylesheets="@../css/style.css" /> </xAxis> @@ -37,28 +37,33 @@ <NumberAxis fx:id="ordAxe" prefHeight="354.0" prefWidth="54.0" side="LEFT" stylesheets="@../css/style.css" /> </yAxis> </ScatterChart> - <Label fx:id="AxesSelected" alignment="CENTER" layoutX="1.0" layoutY="152.0" prefHeight="38.0" prefWidth="672.0"> + <Label fx:id="AxesSelected" alignment="CENTER" layoutX="73.0" layoutY="152.0" prefHeight="38.0" prefWidth="600.0"> <font> <Font size="21.0" /> </font> </Label> + <VBox layoutY="345.0" prefHeight="78.0" prefWidth="678.0" /> </children> <HBox.margin> <Insets left="10.0" /> </HBox.margin> </AnchorPane> - <Button fx:id="settingsView" mnemonicParsing="false" onAction="#openAxesSetting" stylesheets="@../css/style.css"> - <graphic> - <ImageView fitHeight="100.0" fitWidth="30.0" pickOnBounds="true" preserveRatio="true"> - <image> - <Image url="@gear.png" /> - </image> - </ImageView> - </graphic> - <HBox.margin> + <VBox alignment="TOP_CENTER" prefHeight="200.0" prefWidth="100.0" spacing="20.0"> + <children> + <Button fx:id="settingsView" mnemonicParsing="false" onAction="#openAxesSetting" stylesheets="@../css/style.css"> + <graphic> + <ImageView fitHeight="100.0" fitWidth="30.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@gear.png" /> + </image> + </ImageView> + </graphic> + </Button> + </children> + <padding> <Insets top="20.0" /> - </HBox.margin> - </Button> + </padding> + </VBox> </children> </HBox> <VBox alignment="TOP_CENTER" prefHeight="128.0" prefWidth="775.0"> @@ -73,7 +78,7 @@ <font> <Font name="System Bold" size="14.0" /> </font></Button> - <Button fx:id="classifyData" disable="true" mnemonicParsing="false" onAction="#classifyDatas" prefHeight="30.0" prefWidth="174.0" stylesheets="@../css/style.css" text="Classifier les données" textFill="WHITE"> + <Button fx:id="classifyData" disable="true" mnemonicParsing="false" onAction="#openClassification" prefHeight="30.0" prefWidth="174.0" stylesheets="@../css/style.css" text="Classifier les données" textFill="WHITE"> <font> <Font name="System Bold" size="14.0" /> </font></Button> diff --git a/src/main/java/fr/univlille/sae/classification/controller/AxesSettingsController.java b/src/main/java/fr/univlille/sae/classification/controller/AxesSettingsController.java index 88b726cd126eba3e81c11d587905bfb035586b91..b355ba3bb9c6528d2475e76edadb5ee4fd29bc08 100644 --- a/src/main/java/fr/univlille/sae/classification/controller/AxesSettingsController.java +++ b/src/main/java/fr/univlille/sae/classification/controller/AxesSettingsController.java @@ -2,8 +2,11 @@ package fr.univlille.sae.classification.controller; import fr.univlille.sae.classification.view.DataVisualizationView; import javafx.fxml.FXML; +import javafx.scene.chart.NumberAxis; import javafx.scene.control.Button; import javafx.scene.control.ChoiceBox; +import javafx.scene.control.Spinner; +import javafx.scene.control.SpinnerValueFactory; import javafx.stage.Stage; /** @@ -19,6 +22,18 @@ public class AxesSettingsController{ @FXML ChoiceBox selectAbs; + @FXML + Spinner OrdSizeLower; + + @FXML + Spinner OrdSizeUpper; + + @FXML + Spinner AbsSizeUpper; + + @FXML + Spinner AbsSizeLower; + /** * DataVisualizationView associé au controlleur @@ -45,6 +60,22 @@ public class AxesSettingsController{ selectAbs.setValue(dataVisualizationView.getActualX()); } + public void setOrdSizeUpper(double value){ + OrdSizeUpper.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(0.0, 9999, value,1)); + } + + public void setOrdSizeLower(double value){ + OrdSizeLower.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(0.0, 9999, value,1)); + } + + public void setAbsSizeUpper(double value){ + AbsSizeUpper.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(0.0, 9999, value,1)); + } + + public void setAbsSizeLower(double value){ + AbsSizeLower.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(0.0, 9999, value,1)); + } + /** * Méthode permettante d'attribuer la dataVisualizationView associer à la classe * @param dataVisualizationView dataVisualizationView à attribuer @@ -57,11 +88,49 @@ public class AxesSettingsController{ * Validation des paramètres des axes */ public void validate(){ - dataVisualizationView.setActualX(selectAbs.getValue().toString()); - dataVisualizationView.setActualY(selectOrd.getValue().toString()); - dataVisualizationView.getScatterChart().getXAxis().setLabel(dataVisualizationView.getActualX()); - dataVisualizationView.getScatterChart().getYAxis().setLabel(dataVisualizationView.getActualY()); + if(dataVisualizationView.getActualX() != null || dataVisualizationView.getActualY() != null){ + if(!dataVisualizationView.getActualX().equals(selectAbs.getValue().toString()) || + !dataVisualizationView.getActualY().equals(selectOrd.getValue().toString())){ + dataVisualizationView.setActualX(selectAbs.getValue().toString()); + dataVisualizationView.setActualY(selectOrd.getValue().toString()); + + dataVisualizationView.getScatterChart().getXAxis().setLabel(dataVisualizationView.getActualX()); + dataVisualizationView.getScatterChart().getYAxis().setLabel(dataVisualizationView.getActualY()); + reset(); + } + }else{ + dataVisualizationView.setActualX(selectAbs.getValue().toString()); + dataVisualizationView.setActualY(selectOrd.getValue().toString()); + + dataVisualizationView.getScatterChart().getXAxis().setLabel(dataVisualizationView.getActualX()); + dataVisualizationView.getScatterChart().getYAxis().setLabel(dataVisualizationView.getActualY()); + } + + + if((Double)AbsSizeUpper.getValue() != ((NumberAxis)dataVisualizationView.getScatterChart().getXAxis()).getUpperBound() || + (Double)OrdSizeUpper.getValue() != ((NumberAxis)dataVisualizationView.getScatterChart().getYAxis()).getUpperBound() || + (Double)AbsSizeLower.getValue() != ((NumberAxis)dataVisualizationView.getScatterChart().getXAxis()).getLowerBound() || + (Double)OrdSizeLower.getValue() != ((NumberAxis)dataVisualizationView.getScatterChart().getYAxis()).getLowerBound() + ){ + ((NumberAxis) dataVisualizationView.getScatterChart().getXAxis()).setAutoRanging(false); + ((NumberAxis) dataVisualizationView.getScatterChart().getYAxis()).setAutoRanging(false); + + ((NumberAxis) dataVisualizationView.getScatterChart().getXAxis()).setUpperBound((Double) AbsSizeUpper.getValue()); + ((NumberAxis) dataVisualizationView.getScatterChart().getYAxis()).setUpperBound((Double) OrdSizeUpper.getValue()); + + ((NumberAxis) dataVisualizationView.getScatterChart().getXAxis()).setLowerBound((Double) AbsSizeLower.getValue()); + ((NumberAxis) dataVisualizationView.getScatterChart().getYAxis()).setLowerBound((Double) OrdSizeLower.getValue()); + } + + dataVisualizationView.reload(); + reset(); + + stage.close(); + } + public void reset(){ + ((NumberAxis) dataVisualizationView.getScatterChart().getXAxis()).setAutoRanging(true); + ((NumberAxis) dataVisualizationView.getScatterChart().getYAxis()).setAutoRanging(true); dataVisualizationView.reload(); stage.close(); } diff --git a/src/main/java/fr/univlille/sae/classification/controller/DataStageController.java b/src/main/java/fr/univlille/sae/classification/controller/DataStageController.java index 829e3997056e62b3993d54f69488786e2d3b19c3..94c7e97cc0273ca8d7e2ea6367e0e7cc9554a1f3 100644 --- a/src/main/java/fr/univlille/sae/classification/controller/DataStageController.java +++ b/src/main/java/fr/univlille/sae/classification/controller/DataStageController.java @@ -4,6 +4,7 @@ import fr.univlille.sae.classification.model.ClassificationModel; import fr.univlille.sae.classification.view.AxesSettingsView; import fr.univlille.sae.classification.view.DataStageView; import javafx.fxml.FXML; +import javafx.scene.chart.NumberAxis; import javafx.scene.chart.ScatterChart; import javafx.scene.control.Label; import javafx.scene.control.ListView; @@ -32,6 +33,18 @@ public class DataStageController { */ private DataStageView dataStageView; + private double initialX; + private double initialY; + private double initialLowerBoundX; + private double initialUpperBoundX; + private double initialLowerBoundY; + private double initialUpperBoundY; + + public void initialize() { + setupZoom(); + setupDrag(); + } + /** * Ouvrir les paramètres des axes de la vue */ @@ -65,8 +78,89 @@ public class DataStageController { this.AxesSelected.setText(texte); } + public void setAxesSelectedDisable(){ + this.AxesSelected.setDisable(true); + } + public ListView getPointInfo(){ return this.PointInfo; }; + private void setupZoom() { + NumberAxis xAxis = (NumberAxis) scatterChart.getXAxis(); + NumberAxis yAxis = (NumberAxis) scatterChart.getYAxis(); + + scatterChart.setOnScroll(event -> { + xAxis.setAutoRanging(false); + yAxis.setAutoRanging(false); + + double delta = event.getDeltaY(); + double mouseX = event.getSceneX(); + double mouseY = event.getSceneY(); + + double chartX = xAxis.sceneToLocal(mouseX, mouseY).getX(); + double chartY = yAxis.sceneToLocal(mouseX, mouseY).getY(); + + double zoomFactor; + if (delta > 0) { + zoomFactor = 0.90; + } else { + zoomFactor = 1.05; + } + + double xLower = xAxis.getLowerBound(); + double xUpper = xAxis.getUpperBound(); + double yLower = yAxis.getLowerBound(); + double yUpper = yAxis.getUpperBound(); + + double rangeX = xUpper - xLower; + double rangeY = yUpper - yLower; + + double newRangeX = rangeX * zoomFactor; + double newRangeY = rangeY * zoomFactor; + + xAxis.setLowerBound(xLower + (chartX / xAxis.getWidth()) * (rangeX - newRangeX)); + xAxis.setUpperBound(xUpper - ((xAxis.getWidth() - chartX) / xAxis.getWidth()) * (rangeX - newRangeX)); + + yAxis.setLowerBound(yLower + ((yAxis.getHeight() - chartY) / yAxis.getHeight()) * (rangeY - newRangeY)); + yAxis.setUpperBound(yUpper - (chartY / yAxis.getHeight()) * (rangeY - newRangeY)); + }); + + xAxis.setAutoRanging(true); + yAxis.setAutoRanging(true); + } + + private void setupDrag() { + scatterChart.setOnMousePressed(event -> { + initialX = event.getSceneX(); + initialY = event.getSceneY(); + initialLowerBoundX = ((NumberAxis) scatterChart.getXAxis()).getLowerBound(); + initialUpperBoundX = ((NumberAxis) scatterChart.getXAxis()).getUpperBound(); + initialLowerBoundY = ((NumberAxis) scatterChart.getYAxis()).getLowerBound(); + initialUpperBoundY = ((NumberAxis) scatterChart.getYAxis()).getUpperBound(); + }); + + NumberAxis xAxis = (NumberAxis) scatterChart.getXAxis(); + NumberAxis yAxis = (NumberAxis) scatterChart.getYAxis(); + + scatterChart.setOnMouseDragged(event -> { + xAxis.setAutoRanging(false); + yAxis.setAutoRanging(false); + double deltaX = event.getSceneX() - initialX; + double deltaY = event.getSceneY() - initialY; + + double newLowerBoundX = initialLowerBoundX - deltaX * (xAxis.getUpperBound() - xAxis.getLowerBound()) / scatterChart.getWidth(); + double newUpperBoundX = initialUpperBoundX - deltaX * (xAxis.getUpperBound() - xAxis.getLowerBound()) / scatterChart.getWidth(); + double newLowerBoundY = initialLowerBoundY + deltaY * (yAxis.getUpperBound() - yAxis.getLowerBound()) / scatterChart.getHeight(); + double newUpperBoundY = initialUpperBoundY + deltaY * (yAxis.getUpperBound() - yAxis.getLowerBound()) / scatterChart.getHeight(); + + xAxis.setLowerBound(newLowerBoundX); + xAxis.setUpperBound(newUpperBoundX); + yAxis.setLowerBound(newLowerBoundY); + yAxis.setUpperBound(newUpperBoundY); + }); + xAxis.setAutoRanging(true); + yAxis.setAutoRanging(true); + } + } diff --git a/src/main/java/fr/univlille/sae/classification/controller/KNNController.java b/src/main/java/fr/univlille/sae/classification/controller/KNNController.java new file mode 100644 index 0000000000000000000000000000000000000000..def9b3273b96a1a2870403485fa2466c807f98a0 --- /dev/null +++ b/src/main/java/fr/univlille/sae/classification/controller/KNNController.java @@ -0,0 +1,156 @@ +package fr.univlille.sae.classification.controller; + +import fr.univlille.sae.classification.knn.MethodKNN; +import fr.univlille.sae.classification.knn.distance.Distance; +import fr.univlille.sae.classification.knn.distance.DistanceManhattanNormalisee; +import fr.univlille.sae.classification.model.ClassificationModel; +import fr.univlille.sae.classification.model.LoadableData; +import javafx.collections.ObservableList; +import javafx.concurrent.Task; +import javafx.fxml.FXML; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import javafx.stage.Stage; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class KNNController { + + @FXML + private Stage stage; + + @FXML + ChoiceBox<String> algoSelector; + + @FXML + Spinner<Integer> kEntry; + + @FXML + Button autoK; + + @FXML + Button confirmK; + + + @FXML + public void initialize() { + kEntry.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, + (int) Math.sqrt(ClassificationModel.getClassificationModel().getDatas().size()), + 1, + 1)); + + kEntry.getValueFactory().setValue(ClassificationModel.getClassificationModel().getK()); + + algoSelector.getItems().addAll("Euclidienne", "Euclidienne Normalisée", "Manhattan", "Manhattan Normalisée"); + algoSelector.setValue(Distance.getDistanceName(ClassificationModel.getClassificationModel().getDistance())); + } + + + + public void bestK() { + ClassificationModel model = ClassificationModel.getClassificationModel(); + + if(model.getkOptimal() > 0) { + // Le K Optimal à déja été calculé, il n'est pas necessaire de le recaculer. + kEntry.getValueFactory().setValue(model.getkOptimal()); + }else { + // Calcul du K Optimal: + + HBox hBox = new HBox(); + + + Task<Scene> knnTask = new Task<>() { + @Override + protected Scene call() throws Exception { + System.out.println("Call call()"); + updateProgress(0, 3); + updateMessage("Préparation des données "); + + //List<LoadableData> datasShuffle = new ArrayList<>(List.copyOf(model.getDatas())); + // Collections.shuffle(datasShuffle); + MethodKNN.updateModel(model.getDatas()); + Distance dist = Distance.getByName(algoSelector.getValue()); + + updateProgress(1, 3); + updateMessage("Recherche du meilleur K"); + + int bestK = MethodKNN.bestK(model.getDatas(), dist); + + + updateMessage("Test de robustesse"); + updateProgress(2, 3); + + + double robustesse = MethodKNN.robustesse( model.getDatas(), bestK, dist, 0.2); + model.setKOptimal(bestK); + + updateMessage("Affichage du resultat"); + updateProgress(2.5, 3); + + model.setDistance(dist); + + HBox hBox = new HBox(); + Label label = new Label("Best K: " + bestK + " robustesse : " + robustesse); + hBox.getChildren().add(label); + Scene scene = new Scene(hBox); + kEntry.getValueFactory().setValue(bestK); + + updateMessage("Finished"); + updateProgress(3, 3); + + return scene; + } + }; + + ProgressBar pBar = new ProgressBar(); + pBar.progressProperty().bind(knnTask.progressProperty()); + Label statusLabel = new Label(); + statusLabel.textProperty().bind(knnTask.messageProperty()); + + + hBox.getChildren().addAll(statusLabel, pBar); + Stage stageLoad = new Stage(); + Scene scene = new Scene(hBox); + + stageLoad.setScene(scene); + stageLoad.show(); + + Stage stageFinished = new Stage(); + + knnTask.setOnSucceeded(e -> { + stageLoad.close(); + stageFinished.setScene(knnTask.getValue()); + stageFinished.show(); + + }); + knnTask.run(); + //new Thread(knnTask).start(); + + + + + } + + } + + + public void validate() { + + ClassificationModel model = ClassificationModel.getClassificationModel(); + + + int k = kEntry.getValue(); + Distance dist = Distance.getByName(algoSelector.getValue()); + + model.setDistance(dist); + model.setK(k); + model.classifierDonnees(); + + stage.close(); + + } + +} diff --git a/src/main/java/fr/univlille/sae/classification/controller/LoadDataController.java b/src/main/java/fr/univlille/sae/classification/controller/LoadDataController.java index a8b099878bb1e0758fd6ce577e6e2c680087ea82..014af783059a9eb43473293a24c3d30af44c39c2 100644 --- a/src/main/java/fr/univlille/sae/classification/controller/LoadDataController.java +++ b/src/main/java/fr/univlille/sae/classification/controller/LoadDataController.java @@ -1,9 +1,11 @@ package fr.univlille.sae.classification.controller; import fr.univlille.sae.classification.model.ClassificationModel; +import fr.univlille.sae.classification.model.DataType; import javafx.fxml.FXML; import javafx.scene.control.Alert; import javafx.scene.control.Button; +import javafx.scene.control.ChoiceBox; import javafx.scene.control.TextField; import javafx.stage.FileChooser; import javafx.stage.Stage; @@ -21,11 +23,19 @@ public class LoadDataController { @FXML TextField filePath; + @FXML + ChoiceBox<DataType> fileType = new ChoiceBox<>(); + /** * Fichier sélectionné */ File file; + @FXML + public void initialize() { + fileType.getItems().addAll(DataType.values()); + } + /** * Ouvre un explorateur de fichiers pour sélectionner le fichier à étudier */ @@ -40,12 +50,17 @@ public class LoadDataController { } + + /** * Valide le fichier sélectionné au préalable */ public void validate(){ - if (file == null || file.isDirectory() || !file.exists()) { + DataType typeChoisi = fileType.getValue(); + + + if (file == null || file.isDirectory() || !file.exists() || fileType.getValue() == null) { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle("Erreur de chargement du fichier"); alert.setHeaderText(null); @@ -56,6 +71,7 @@ public class LoadDataController { return; } + ClassificationModel.getClassificationModel().setType(typeChoisi); ClassificationModel.getClassificationModel().loadData(file); stage.close(); } diff --git a/src/main/java/fr/univlille/sae/classification/controller/MainStageController.java b/src/main/java/fr/univlille/sae/classification/controller/MainStageController.java index 3aa454ca6d45d0b7c3facb845f0663c6d6afbebf..d18c02d73d9952ef1fb300ddde0731b560ace749 100644 --- a/src/main/java/fr/univlille/sae/classification/controller/MainStageController.java +++ b/src/main/java/fr/univlille/sae/classification/controller/MainStageController.java @@ -3,6 +3,7 @@ package fr.univlille.sae.classification.controller; import fr.univlille.sae.classification.model.ClassificationModel; import fr.univlille.sae.classification.view.*; import javafx.fxml.FXML; +import javafx.scene.chart.NumberAxis; import javafx.scene.chart.ScatterChart; import javafx.scene.control.Button; import javafx.scene.control.Label; @@ -30,6 +31,18 @@ public class MainStageController { private MainStageView mainStageView; + private double initialX; + private double initialY; + private double initialLowerBoundX; + private double initialUpperBoundX; + private double initialLowerBoundY; + private double initialUpperBoundY; + + public void initialize() { + setupZoom(); + setupDrag(); + } + /** * Ouvre l'interface de chargement des données. * Permet à l'utilisateur de sélectionner des données à charger pour la classification. @@ -39,6 +52,11 @@ public class MainStageController { loadDataView.show(); } + public void openClassification(){ + KNNView knnView = new KNNView(ClassificationModel.getClassificationModel(), stage); + knnView.show(); + } + /** * Ouvre l'interface d'une nouvelle vue. * Affiche une nouvelle fenêtre pour visualiser les données après classification. @@ -99,6 +117,10 @@ public class MainStageController { this.AxesSelected.setText(texte); } + public void setAxesSelectedDisable(){ + this.AxesSelected.setDisable(true); + } + /** * Renvoie le bouton de classification de données. * @return Bouton utilisé pour déclencher la classification des données. @@ -110,4 +132,83 @@ public class MainStageController { public ListView getPointInfo(){ return this.PointInfo; }; + + private void setupZoom() { + NumberAxis xAxis = (NumberAxis) scatterChart.getXAxis(); + NumberAxis yAxis = (NumberAxis) scatterChart.getYAxis(); + + scatterChart.setOnScroll(event -> { + xAxis.setAutoRanging(false); + yAxis.setAutoRanging(false); + + double delta = event.getDeltaY(); + double mouseX = event.getSceneX(); + double mouseY = event.getSceneY(); + + double chartX = xAxis.sceneToLocal(mouseX, mouseY).getX(); + double chartY = yAxis.sceneToLocal(mouseX, mouseY).getY(); + + double zoomFactor; + if (delta > 0) { + zoomFactor = 0.90; + } else { + zoomFactor = 1.05; + } + + double xLower = xAxis.getLowerBound(); + double xUpper = xAxis.getUpperBound(); + double yLower = yAxis.getLowerBound(); + double yUpper = yAxis.getUpperBound(); + + double rangeX = xUpper - xLower; + double rangeY = yUpper - yLower; + + double newRangeX = rangeX * zoomFactor; + double newRangeY = rangeY * zoomFactor; + + xAxis.setLowerBound(xLower + (chartX / xAxis.getWidth()) * (rangeX - newRangeX)); + xAxis.setUpperBound(xUpper - ((xAxis.getWidth() - chartX) / xAxis.getWidth()) * (rangeX - newRangeX)); + + yAxis.setLowerBound(yLower + ((yAxis.getHeight() - chartY) / yAxis.getHeight()) * (rangeY - newRangeY)); + yAxis.setUpperBound(yUpper - (chartY / yAxis.getHeight()) * (rangeY - newRangeY)); + }); + + xAxis.setAutoRanging(true); + yAxis.setAutoRanging(true); + } + + + private void setupDrag() { + scatterChart.setOnMousePressed(event -> { + initialX = event.getSceneX(); + initialY = event.getSceneY(); + initialLowerBoundX = ((NumberAxis) scatterChart.getXAxis()).getLowerBound(); + initialUpperBoundX = ((NumberAxis) scatterChart.getXAxis()).getUpperBound(); + initialLowerBoundY = ((NumberAxis) scatterChart.getYAxis()).getLowerBound(); + initialUpperBoundY = ((NumberAxis) scatterChart.getYAxis()).getUpperBound(); + }); + + NumberAxis xAxis = (NumberAxis) scatterChart.getXAxis(); + NumberAxis yAxis = (NumberAxis) scatterChart.getYAxis(); + + scatterChart.setOnMouseDragged(event -> { + xAxis.setAutoRanging(false); + yAxis.setAutoRanging(false); + double deltaX = event.getSceneX() - initialX; + double deltaY = event.getSceneY() - initialY; + + double newLowerBoundX = initialLowerBoundX - deltaX * (xAxis.getUpperBound() - xAxis.getLowerBound()) / scatterChart.getWidth(); + double newUpperBoundX = initialUpperBoundX - deltaX * (xAxis.getUpperBound() - xAxis.getLowerBound()) / scatterChart.getWidth(); + double newLowerBoundY = initialLowerBoundY + deltaY * (yAxis.getUpperBound() - yAxis.getLowerBound()) / scatterChart.getHeight(); + double newUpperBoundY = initialUpperBoundY + deltaY * (yAxis.getUpperBound() - yAxis.getLowerBound()) / scatterChart.getHeight(); + + xAxis.setLowerBound(newLowerBoundX); + xAxis.setUpperBound(newUpperBoundX); + yAxis.setLowerBound(newLowerBoundY); + yAxis.setUpperBound(newUpperBoundY); + }); + xAxis.setAutoRanging(true); + yAxis.setAutoRanging(true); + } + } diff --git a/src/main/java/fr/univlille/sae/classification/knn/MethodKNN.java b/src/main/java/fr/univlille/sae/classification/knn/MethodKNN.java index b4deb89f301bf6126622078a84bc839b8f5a8fb1..ce462a0b5c12e72f1ac2d9de29bed32aee1006a3 100644 --- a/src/main/java/fr/univlille/sae/classification/knn/MethodKNN.java +++ b/src/main/java/fr/univlille/sae/classification/knn/MethodKNN.java @@ -1,7 +1,6 @@ package fr.univlille.sae.classification.knn; -import fr.univlille.sae.classification.knn.distance.Distance; -import fr.univlille.sae.classification.knn.distance.DistanceEuclidienneNormalisee; +import fr.univlille.sae.classification.knn.distance.*; import fr.univlille.sae.classification.model.ClassificationModel; import fr.univlille.sae.classification.model.DataType; import fr.univlille.sae.classification.model.LoadableData; @@ -66,6 +65,7 @@ public class MethodKNN { // On recupere les K voisions de data. List<LoadableData> kVoisins = MethodKNN.kVoisins(datas, data, k, distance); + System.out.println("Neighbours: " + kVoisins); // System.out.println("Neighbours found : " + kVoisins); @@ -91,8 +91,7 @@ public class MethodKNN { public static int bestK(List<LoadableData> datas, Distance distance) { - //ToDO Juste pour eviter d'avoir k = 35 je limite la taille max de K. Je vais chercher si y'a une methode particuliere pour limiter sa taille - int maxK = (int) (Math.sqrt(datas.size())/2 *2); + int maxK = (int) (Math.sqrt(datas.size())); System.out.println("Max k: " + maxK); Map<Integer, Double> results = new HashMap<>(); @@ -111,31 +110,44 @@ public class MethodKNN { public static double robustesse(List<LoadableData> datas, int k, Distance distance, double testPart) { - int totalFind = 0; - int totalTry = 0; - // On calcul la robusstesse en utilisant testPart% du fichier de base comme donnée a tester. - int partSize = (int) (datas.size() * testPart); - List<LoadableData> trainingData = new ArrayList<>(List.copyOf(datas.subList(0, datas.size()-partSize))); - List<LoadableData> testData = List.copyOf(datas.subList(datas.size()-partSize, datas.size())); + double taux = 0; - // On met a jour l'algo avec les nouvelles données (permet de re-calculer l'amplitude ainsi que les val max et min - updateModel(trainingData); + for(int i = 0; i<(int)1/testPart; i++) { - // On estime la classe chaque donnée de test, et on verifie si l'algo a bon - for(LoadableData l : testData) { - totalTry++; - String baseClass = l.getClassification(); - // System.out.println("Base class : " + baseClass); - // System.out.println("Base data: " + l); - if(baseClass.equals(MethodKNN.estimateClass(trainingData,l, k, distance))) totalFind++; + int totalFind = 0; + int totalTry = 0; + + // On calcul la robusstesse en utilisant testPart% du fichier de base comme donnée a tester. + int partSize = (int) (datas.size() * testPart); + List<LoadableData> testData = List.copyOf(datas.subList(i*partSize, (i*partSize)+partSize)); + List<LoadableData> trainingData = new ArrayList<>(List.copyOf(datas)); + trainingData.removeAll(testData); + + // On met a jour l'algo avec les nouvelles données (permet de re-calculer l'amplitude ainsi que les val max et min + updateModel(trainingData); + + // On estime la classe chaque donnée de test, et on verifie si l'algo a bon + for(LoadableData l : testData) { + totalTry++; + String baseClass = l.getClassification(); + // System.out.println("Base class : " + baseClass); + // System.out.println("Base data: " + l); + if(baseClass.equals(MethodKNN.estimateClass(trainingData,l, k, distance))) totalFind++; + + } + + + // On affiche le taux de reussite a chaque tour + System.out.println("total find: " +totalFind + " total try: " + totalTry); + taux += (totalFind/(double) totalTry); } - // On return le taux de reussite - System.out.println("total find: " +totalFind + " total try: " + totalTry); - return (totalFind/(double) totalTry); + + + return taux/(1/testPart); } public static void main(String[] args) { @@ -144,8 +156,8 @@ public class MethodKNN { ClassificationModel model = ClassificationModel.getClassificationModel(); - model.setType(DataType.POKEMON); - model.loadData(new File(path+"data/pokemon_train.csv")); + model.setType(DataType.IRIS); + model.loadData(new File(path+"data/iris.csv")); MethodKNN.updateModel(model.getDatas()); System.out.println(); @@ -153,14 +165,18 @@ public class MethodKNN { // On mélange les données pour tester sur differentes variétes car le fichier de base est trié. Collections.shuffle(datas); - System.out.println("Search best k"); + for(int i = 0; i<1; i++) { + System.out.println("Search best k"); - // On cherche le meilleure K - int bestK = MethodKNN.bestK(datas, new DistanceEuclidienneNormalisee()); - System.out.println(bestK); + // On cherche le meilleure K + int bestK = MethodKNN.bestK(datas, new DistanceManhattanNormalisee()); + System.out.println(bestK); + + // Puis on clacul la robustesse avec le K trouvé + System.out.println(MethodKNN.robustesse( datas, bestK, new DistanceManhattanNormalisee(), 0.2)); + + } - // Puis on clacul la robustesse avec le K trouvé - System.out.println(MethodKNN.robustesse( datas, bestK, new DistanceEuclidienneNormalisee(), 0.2)); } diff --git a/src/main/java/fr/univlille/sae/classification/knn/distance/Distance.java b/src/main/java/fr/univlille/sae/classification/knn/distance/Distance.java index 9f4be22c476edafedb6efc223e33c5577b3d939d..24fd3796dd99cc4eb343b15cc679a407b90eac05 100644 --- a/src/main/java/fr/univlille/sae/classification/knn/distance/Distance.java +++ b/src/main/java/fr/univlille/sae/classification/knn/distance/Distance.java @@ -7,4 +7,30 @@ public interface Distance { double distance(LoadableData l1, LoadableData l2); + static Distance getByName(String name){ + switch (name) { + case "Euclidienne Normalisée": + return new DistanceEuclidienneNormalisee(); + case "Manhattan": + return new DistanceManhattan(); + case "Manhattan Normalisée": + return new DistanceManhattanNormalisee(); + default: + return new DistanceEuclidienne(); + } + + } + + static String getDistanceName(Distance distance){ + if (distance instanceof DistanceEuclidienneNormalisee) { + return "Euclidienne Normalisee"; + }else if (distance instanceof DistanceManhattan){ + return "Manhattan"; + }else if (distance instanceof DistanceManhattanNormalisee){ + return "ManhattanNormalisee"; + }else { + return "Euclidienne"; + } + } + } diff --git a/src/main/java/fr/univlille/sae/classification/knn/distance/DistanceEuclidienneNormalisee.java b/src/main/java/fr/univlille/sae/classification/knn/distance/DistanceEuclidienneNormalisee.java index 5c0b54966dcbbd86e72304ca18b88a7b6dbf10fc..9be1180ffd7a53d7b2b2d785596fdbfcea7dbefc 100644 --- a/src/main/java/fr/univlille/sae/classification/knn/distance/DistanceEuclidienneNormalisee.java +++ b/src/main/java/fr/univlille/sae/classification/knn/distance/DistanceEuclidienneNormalisee.java @@ -14,7 +14,8 @@ public class DistanceEuclidienneNormalisee implements Distance{ double[] normaliseL1 = normalise(l1); double[] normaliseL2 = normalise(l2); for(int i = 0;i<normaliseL1.length;i++) { - total += Math.pow(normaliseL2[i] - normaliseL1[i], 2); + //total += Math.pow(normaliseL2[i] - normaliseL1[i], 2); + total += Math.pow((l2.getAttributes()[i] - l1.getAttributes()[i])/MethodKNN.amplitude[i], 2); } //A Check for(int i = 0;i<l2.getStringAttributes().length;i++) { diff --git "a/src/main/java/fr/univlille/sae/classification/knn/distance/DistanceManhattanNormalis\303\251e.java" b/src/main/java/fr/univlille/sae/classification/knn/distance/DistanceManhattanNormalisee.java similarity index 89% rename from "src/main/java/fr/univlille/sae/classification/knn/distance/DistanceManhattanNormalis\303\251e.java" rename to src/main/java/fr/univlille/sae/classification/knn/distance/DistanceManhattanNormalisee.java index 60c56d4539c38c1acebd92366765f11fe41b049e..50a678bf52e356b3388b9145b072340f61b72d17 100644 --- "a/src/main/java/fr/univlille/sae/classification/knn/distance/DistanceManhattanNormalis\303\251e.java" +++ b/src/main/java/fr/univlille/sae/classification/knn/distance/DistanceManhattanNormalisee.java @@ -4,14 +4,12 @@ import fr.univlille.sae.classification.knn.MethodKNN; import fr.univlille.sae.classification.model.LoadableData; -public class DistanceManhattanNormalisée implements Distance{ +public class DistanceManhattanNormalisee implements Distance{ @Override public double distance(LoadableData l1, LoadableData l2) { double distance = 0; - - for(int i = 0 ;i<l1.getAttributes().length; i++){ double dPoids = (Math.abs(l1.getAttributes()[i]- l2.getAttributes()[i])- MethodKNN.minData[i])/MethodKNN.amplitude[i]; distance = distance + dPoids; diff --git a/src/main/java/fr/univlille/sae/classification/model/ClassificationModel.java b/src/main/java/fr/univlille/sae/classification/model/ClassificationModel.java index f4cf1189e09c4528f7ffdce333447048d9640cf4..0c5291ca16dadd29e930f2346bec1219feb9c5f5 100644 --- a/src/main/java/fr/univlille/sae/classification/model/ClassificationModel.java +++ b/src/main/java/fr/univlille/sae/classification/model/ClassificationModel.java @@ -3,6 +3,8 @@ package fr.univlille.sae.classification.model; import com.opencsv.bean.CsvToBeanBuilder; import fr.univlille.sae.classification.knn.MethodKNN; import fr.univlille.sae.classification.knn.distance.Distance; +import fr.univlille.sae.classification.knn.distance.DistanceEuclidienne; +import fr.univlille.sae.classification.knn.distance.DistanceManhattan; import fr.univlille.sae.classification.utils.Observable; import java.io.File; @@ -26,6 +28,7 @@ public class ClassificationModel extends Observable { private Distance distance; private int kOptimal; + private int k; /** * Renvoie une instance unique du modèle. Par défaut, le type de ce modèle est Iris. @@ -52,6 +55,9 @@ public class ClassificationModel extends Observable { this.datas = new ArrayList<>(); this.dataToClass = new ConcurrentHashMap<>(); this.type = type; + this.kOptimal = 0; + this.k = 0; + this.distance = new DistanceEuclidienne(); } /** * Ajoute un point au nuage de points avec toutes les données de ce point. @@ -85,6 +91,8 @@ public class ClassificationModel extends Observable { types.add(d.getClassification()); } + Collections.shuffle(datas); + LoadableData.setClassificationTypes(types); notifyObservers(); } catch (IOException e) { @@ -92,6 +100,7 @@ public class ClassificationModel extends Observable { } } + /** * Classifie toutes les données à classifier. * Parcourt la liste des données à classifier et appelle la méthode pour chaque donnée. @@ -107,11 +116,8 @@ public class ClassificationModel extends Observable { */ public void classifierDonnee(LoadableData data) { if(dataToClass.get(data) != null && dataToClass.get(data)) return; - List<String> classes = new ArrayList<>(LoadableData.getClassificationTypes()); - - - - data.setClassification(MethodKNN.estimateClass(datas, data, 1, distance)); + this.dataToClass.remove(data); + data.setClassification(MethodKNN.estimateClass(datas, data, kOptimal, distance)); notifyObservers(data); dataToClass.put(data, true); } @@ -141,6 +147,18 @@ public class ClassificationModel extends Observable { return kOptimal; } + public void setKOptimal(int kOptimal) { + this.kOptimal = kOptimal; + } + + public int getK() { + return k; + } + + public void setK(int k) { + this.k = k; + } + /** * Renvoie la liste des données chargées. * @return liste des données chargées. diff --git a/src/main/java/fr/univlille/sae/classification/model/Iris.java b/src/main/java/fr/univlille/sae/classification/model/Iris.java index 6d097073f0d2ee67b1440817813c8c785b5634e8..27d271a25e4898f4b7eeaa38beaf45610cd686a5 100644 --- a/src/main/java/fr/univlille/sae/classification/model/Iris.java +++ b/src/main/java/fr/univlille/sae/classification/model/Iris.java @@ -23,6 +23,23 @@ public class Iris extends LoadableData { @CsvBindByName(column = "variety") private String variety; + /** + * Constructeur pour créer une instance de Iris avec tous les attributs. + * @param sepalLength longueur du sépale. + * @param sepalWidth largeur du sépale. + * @param petalLength longueur du pétale. + * @param petalWidth largeur du pétale. + * @param variety variété de l'Iris. + */ + public Iris(double sepalLength, double sepalWidth, double petalLength, double petalWidth, String variety) { + super(); + this.sepalWidth = sepalWidth; + this.sepalLength = sepalLength; + this.petalWidth = petalWidth; + this.petalLength = petalLength; + this.variety = variety; + } + /** * Constructeur pour créer une instance de Iris avec les dimensions des sépales et des pétales. * @param sepalLength longueur du sépale. @@ -59,23 +76,6 @@ public class Iris extends LoadableData { this.variety = classification; } - /** - * Constructeur pour créer une instance de Iris avec tous les attributs. - * @param sepalLength longueur du sépale. - * @param sepalWidth largeur du sépale. - * @param petalLength longueur du pétale. - * @param petalWidth largeur du pétale. - * @param variety variété de l'Iris. - */ - public Iris(double sepalLength, double sepalWidth, double petalLength, double petalWidth, String variety) { - super(); - this.sepalWidth = sepalWidth; - this.sepalLength = sepalLength; - this.petalWidth = petalWidth; - this.petalLength = petalLength; - this.variety = variety; - } - /** * Renvoie la largeur du sépale. * @return largeur du sépale. diff --git a/src/main/java/fr/univlille/sae/classification/model/Pokemon.java b/src/main/java/fr/univlille/sae/classification/model/Pokemon.java index a2b80ddf6bbdca3dd943cdfb8bdbd1b9dbef85e1..63c236b479d1c5b4730d4e8956799d0f0c7658c1 100644 --- a/src/main/java/fr/univlille/sae/classification/model/Pokemon.java +++ b/src/main/java/fr/univlille/sae/classification/model/Pokemon.java @@ -204,7 +204,7 @@ public class Pokemon extends LoadableData { @Override public String[] getStringAttributes() { - return new String[0]; + return new String[]{name, type2, String.valueOf(isLegendary)}; } @Override diff --git a/src/main/java/fr/univlille/sae/classification/view/AxesSettingsView.java b/src/main/java/fr/univlille/sae/classification/view/AxesSettingsView.java index 59a46de66948e25c282f97f29c3dd2842207e047..7100e254021975aede6f6af63f3e6b676c7fa479 100644 --- a/src/main/java/fr/univlille/sae/classification/view/AxesSettingsView.java +++ b/src/main/java/fr/univlille/sae/classification/view/AxesSettingsView.java @@ -4,6 +4,7 @@ import fr.univlille.sae.classification.controller.AxesSettingsController; import fr.univlille.sae.classification.model.ClassificationModel; import fr.univlille.sae.classification.model.LoadableData; import javafx.fxml.FXMLLoader; +import javafx.scene.chart.NumberAxis; import javafx.scene.control.Alert; import javafx.stage.Modality; import javafx.stage.Stage; @@ -70,6 +71,12 @@ public class AxesSettingsView { controller.setSelectAbs(dataType.getAttributesNames().keySet().toArray(new String[0])); controller.setSelectOrd(dataType.getAttributesNames().keySet().toArray(new String[0])); + controller.setAbsSizeUpper(((NumberAxis)dataVisualizationView.getScatterChart().getXAxis()).getUpperBound()); + controller.setAbsSizeLower(((NumberAxis)dataVisualizationView.getScatterChart().getXAxis()).getLowerBound()); + + controller.setOrdSizeUpper(((NumberAxis)dataVisualizationView.getScatterChart().getYAxis()).getUpperBound()); + controller.setOrdSizeLower(((NumberAxis)dataVisualizationView.getScatterChart().getYAxis()).getLowerBound()); + root.showAndWait(); } catch (IOException e) { System.out.println("Erreur lors du chargement de la scène : " + e.getMessage()); diff --git a/src/main/java/fr/univlille/sae/classification/view/DataStageView.java b/src/main/java/fr/univlille/sae/classification/view/DataStageView.java index 1c461d5b72619f2d366ae4ef14a2614b66be4503..2325405ffc564ddab0b157577d175e7f577dbf27 100644 --- a/src/main/java/fr/univlille/sae/classification/view/DataStageView.java +++ b/src/main/java/fr/univlille/sae/classification/view/DataStageView.java @@ -111,6 +111,7 @@ public class DataStageView extends DataVisualizationView implements Observer { controller.setAxesSelected("Aucuns axes sélectionnés"); } else { controller.setAxesSelected(""); + controller.setAxesSelectedDisable(); List<LoadableData> points = new ArrayList<>(model.getDatas()); points.addAll(model.getDataToClass().keySet()); @@ -123,6 +124,9 @@ public class DataStageView extends DataVisualizationView implements Observer { if(editSerie == null){ editSerie = new ScatterChart.Series<Double, Double>(); } + if(data.getClassification().equals("undefined") || model.getDataToClass().containsKey(data)) { + nodePoint = ViewUtil.getForm(data, new Rectangle(10,10), controller); + } dataPoint.setNode(nodePoint); editSerie.getData().add(dataPoint); serieList.put(data.getClassification(), editSerie); @@ -132,7 +136,6 @@ public class DataStageView extends DataVisualizationView implements Observer { serieList.get(serie).setName(serie); } scatterChart.getData().addAll(serieList.values()); - scatterChart.setLegendVisible(true); } } catch (Exception e) { System.err.println("Erreur de mise à jour : " + e.getMessage()); diff --git a/src/main/java/fr/univlille/sae/classification/view/KNNView.java b/src/main/java/fr/univlille/sae/classification/view/KNNView.java new file mode 100644 index 0000000000000000000000000000000000000000..31cd2982be04ff01e20ba30e7d61a17a087b39d2 --- /dev/null +++ b/src/main/java/fr/univlille/sae/classification/view/KNNView.java @@ -0,0 +1,54 @@ +package fr.univlille.sae.classification.view; + +import fr.univlille.sae.classification.controller.KNNController; +import fr.univlille.sae.classification.controller.LoadDataController; +import fr.univlille.sae.classification.model.ClassificationModel; +import javafx.fxml.FXMLLoader; +import javafx.stage.Modality; +import javafx.stage.Stage; + +import java.io.File; +import java.io.IOException; +import java.net.URL; + +public class KNNView { + + private ClassificationModel model; + private Stage owner; + + /** + * Constructeur de la vue de chargement des données. + * @param model modèle de classification à utiliser. + * @param owner fenêtre parente. + */ + public KNNView(ClassificationModel model, Stage owner) { + this.model = model; + this.owner = owner; + } + + public void show() { + FXMLLoader loader = new FXMLLoader(); + URL fxmlFileUrl = getClass().getClassLoader().getResource("stages"+ File.separator+"k-NN-stage.fxml"); + + if (fxmlFileUrl == null) { + System.out.println("Impossible de charger le fichier fxml"); + System.exit(-1); + } + + loader.setLocation(fxmlFileUrl); + + try { + Stage root = loader.load(); + + root.setResizable(false); + root.initOwner(owner); + root.initModality(Modality.APPLICATION_MODAL); + root.setTitle("Configuration de la classification"); + KNNController controller = loader.getController(); + + root.showAndWait(); + } catch (IOException e) { + System.out.println("Erreur lors du chargement de la scène : " + e.getMessage()); + } + } +} diff --git a/src/main/java/fr/univlille/sae/classification/view/LoadDataView.java b/src/main/java/fr/univlille/sae/classification/view/LoadDataView.java index 438bba9c5be3c42d8f90c523b642475635a23c30..e7961d593ab7099dbfcf41f7bcf8cd5861e5e7e6 100644 --- a/src/main/java/fr/univlille/sae/classification/view/LoadDataView.java +++ b/src/main/java/fr/univlille/sae/classification/view/LoadDataView.java @@ -1,5 +1,7 @@ package fr.univlille.sae.classification.view; +import fr.univlille.sae.classification.controller.AxesSettingsController; +import fr.univlille.sae.classification.controller.LoadDataController; import fr.univlille.sae.classification.model.ClassificationModel; import javafx.fxml.FXMLLoader; import javafx.stage.Modality; diff --git a/src/main/java/fr/univlille/sae/classification/view/MainStageView.java b/src/main/java/fr/univlille/sae/classification/view/MainStageView.java index 0d14a52416059ae668507fd9bb39a3c68ba0bf96..43ac5a948165c0ff14484f446a762780b2bafb5a 100644 --- a/src/main/java/fr/univlille/sae/classification/view/MainStageView.java +++ b/src/main/java/fr/univlille/sae/classification/view/MainStageView.java @@ -2,13 +2,10 @@ package fr.univlille.sae.classification.view; import fr.univlille.sae.classification.controller.MainStageController; import fr.univlille.sae.classification.model.ClassificationModel; -import fr.univlille.sae.classification.model.DataType; -import fr.univlille.sae.classification.model.Iris; import fr.univlille.sae.classification.model.LoadableData; import fr.univlille.sae.classification.utils.Observable; import fr.univlille.sae.classification.utils.Observer; import fr.univlille.sae.classification.utils.ViewUtil; -import javafx.collections.ObservableList; import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.chart.ScatterChart; @@ -107,15 +104,14 @@ public class MainStageView extends DataVisualizationView implements Observer { return; } - ObservableList<ScatterChart.Series> series = scatterChart.getData(); - for (ScatterChart.Series serie : series) { - serie.getData().clear(); - } + scatterChart.getData().clear(); + serieList.clear(); if (actualX == null && actualY == null) { controller.setAxesSelected("Aucuns axes sélectionnés"); } else { controller.setAxesSelected(""); + controller.setAxesSelectedDisable(); List<LoadableData> points = new ArrayList<>(model.getDatas()); points.addAll(model.getDataToClass().keySet()); @@ -128,6 +124,10 @@ public class MainStageView extends DataVisualizationView implements Observer { if(editSerie == null){ editSerie = new ScatterChart.Series<Double, Double>(); } + if(data.getClassification().equals("undefined") || model.getDataToClass().containsKey(data)) { + nodePoint = ViewUtil.getForm(data, new Rectangle(10,10), controller); + } + dataPoint.setNode(nodePoint); editSerie.getData().add(dataPoint); serieList.put(data.getClassification(), editSerie); @@ -137,7 +137,6 @@ public class MainStageView extends DataVisualizationView implements Observer { serieList.get(serie).setName(serie); } scatterChart.getData().addAll(serieList.values()); - scatterChart.setLegendVisible(true); } } catch (Exception e) { System.err.println("Erreur de mise à jour : " + e.getMessage()); @@ -151,6 +150,9 @@ public class MainStageView extends DataVisualizationView implements Observer { System.err.println("Erreur de mise à jour."); return; } + + + LoadableData newData = (LoadableData) data; if (actualX == null || actualY == null) { controller.setAxesSelected("Aucuns axes sélectionnés");